@@ -7,12 +7,52 @@ const stats = [
77 [ "LOD" , "Min/max buckets" ] ,
88] ;
99
10- const heroBadges = [ "WASM only" , "ImPlot native" , "Jupyter + Web" ] ;
10+ const proofPoints = [
11+ [ "10M" , "line-point workflow target" ] ,
12+ [ "O(px)" , "pan and zoom work per frame" ] ,
13+ [ "0" , "JSON point arrays on the hot path" ] ,
14+ ] ;
15+
16+ const quickstart = [
17+ "pip install nbimplot" ,
18+ "" ,
19+ "import nbimplot as ip" ,
20+ "p = ip.Plot(width=900, height=450, title=\"Signal\")" ,
21+ "h = p.line(\"mid\", y, x=t)" ,
22+ "h.set_data(y_new, x=t_new)" ,
23+ "p.show()" ,
24+ ] ;
1125
12- const heroProofs = [
13- [ "10M" , "line points target" ] ,
14- [ "O(px)" , "interaction cost" ] ,
15- [ "0" , "JS render fallback" ] ,
26+ const webstart = [
27+ "npm install @nbimplot/web" ,
28+ "" ,
29+ "import { createPlot } from \"@nbimplot/web\";" ,
30+ "const plot = await createPlot(host, { title: \"Signal\" });" ,
31+ "const h = plot.line(\"mid\", y, { x: t });" ,
32+ "h.setData(yNext, { x: tNext });" ,
33+ "plot.render();" ,
34+ ] ;
35+
36+ const productPromises = [
37+ "Notebook output cell only" ,
38+ "Strict WASM + ImPlot rendering" ,
39+ "Binary array transport" ,
40+ "LOD inside the core" ,
41+ ] ;
42+
43+ const principles = [
44+ {
45+ title : "Binary data path" ,
46+ text : "NumPy and typed arrays move as buffers. The Python and browser APIs avoid JSON point lists for series data." ,
47+ } ,
48+ {
49+ title : "ImPlot interaction model" ,
50+ text : "Pan, zoom, axis menus, legends, selection, and hover behavior come from the ImGui + ImPlot WASM runtime." ,
51+ } ,
52+ {
53+ title : "Pixel-bounded rendering" ,
54+ text : "Large time series switch to min/max LOD so interaction cost tracks screen resolution, not raw array size." ,
55+ } ,
1656] ;
1757
1858const resourceLinks = [
@@ -29,91 +69,91 @@ const examples = [
2969 id : "line-lod-plot" ,
3070 section : "Performance" ,
3171 title : "Million Point Line + Custom X + LOD" ,
32- text : "Large time-series path using explicit x data , WASM min/max LOD, and callback-driven hover/click/selection inspection." ,
72+ text : "Explicit x/y buffers , WASM min/max LOD, and callback-driven hover/click/selection inspection." ,
3373 code : 'const h = plot.line("signal", y, { x });\nplot.onHover(console.log);\nplot.onSelection((e) => plot.indicesForSelection(e, h));' ,
3474 } ,
3575 {
3676 id : "streaming-plot" ,
3777 section : "Performance" ,
3878 title : "Realtime Streaming Ring Buffer" ,
39- text : "Appends explicit x/y typed-array chunks into a fixed-capacity line with pause/resume controls ." ,
79+ text : "Append explicit x/y chunks into a fixed-capacity line without recreating the plot object ." ,
4080 code : 'const h = plot.streamLine("ticks", { capacity: 12000, x: initialX });\nh.append(chunk, { x: chunkX });\nh.pause(); h.resume();' ,
4181 } ,
4282 {
4383 id : "scatter-plot" ,
4484 section : "Points" ,
4585 title : "Scatter + Bubble Encodings" ,
46- text : "Explicit x/y buffers, marker rendering, and bubble sizes for dense point-cloud style workflows." ,
86+ text : "Point-cloud rendering with explicit x/y data and bubble-size encodings for dense browser workflows." ,
4787 code : 'plot.scatter("samples", y, { x });\nplot.bubbles("volume", y, sizes, { x });' ,
4888 } ,
4989 {
5090 id : "curve-plot" ,
5191 section : "Curves" ,
5292 title : "Stairs, Stems, Digital, Shaded, Error Bars" ,
53- text : "Common signal -analysis overlays in one canvas: stepped series, impulses, digital states, confidence bands, and uncertainty intervals ." ,
93+ text : "Signal -analysis overlays in one canvas: stepped series, impulses, bands, digital states, and uncertainty." ,
5494 code : 'plot.stairs("step", y, { x });\nplot.shaded("band", lower, upper, { x });\nplot.errorBars("fit", y, { x, err });' ,
5595 } ,
5696 {
5797 id : "bars-plot" ,
5898 section : "Categorical" ,
5999 title : "Bars, Grouped Bars, Horizontal Bars" ,
60- text : "Three subplot panels showing vertical bars, grouped categorical bars , and horizontal rankings." ,
100+ text : "Vertical bars, grouped categories , and horizontal rankings across ImPlot subplots ." ,
61101 code : 'plot.setSubplots(1, 3);\nplot.bars("sales", values);\nplot.barGroups(labels, matrix);\nplot.barsH("rank", values);' ,
62102 } ,
63103 {
64104 id : "distribution-plot" ,
65105 section : "Statistics" ,
66106 title : "Histogram + 2D Histogram" ,
67- text : "1D and 2D distributions, including a colorbar for density inspection." ,
107+ text : "1D and 2D distributions with colorbar-backed density inspection." ,
68108 code : 'plot.histogram("returns", values, { bins: 80 });\nplot.histogram2d("density", x, y, { xBins: 80, yBins: 60 });' ,
69109 } ,
70110 {
71111 id : "heatmap-image-plot" ,
72112 section : "Matrices" ,
73113 title : "Heatmap + Image" ,
74- text : "Matrix plotting with empty heatmap labels, colorbar formatting, and a float RGB image buffer ." ,
114+ text : "Matrix and image plotting with empty heatmap labels, colorbar formatting, and float RGB buffers ." ,
75115 code : 'plot.setColormap("Viridis");\nplot.heatmap("z", matrix, { rows, cols, labelFmt: "" });\nplot.image("rgb", image, { rows, cols, channels: 3 });' ,
76116 } ,
77117 {
78118 id : "overlays-plot" ,
79119 section : "Overlays" ,
80120 title : "Annotations, Tags, Text, Infinite Lines, Pie" ,
81- text : "ImPlot overlays for thresholds , labels, callouts, tags, and pie chart composition." ,
121+ text : "Thresholds, callouts , labels, tags, and pie- chart composition using ImPlot primitives ." ,
82122 code : 'plot.vlines("events", xs);\nplot.tagY(0, { labelFmt: "zero" });\nplot.annotation("peak", x, y);\nplot.pieChart("mix", values, { labels });' ,
83123 } ,
84124 {
85125 id : "axes-plot" ,
86126 section : "Axes" ,
87127 title : "Axis Labels, Formats, Ticks, Log Scale, Secondary Axis" ,
88- text : "Secondary y-axis , custom ticks, numeric formatting, and log scaling from the same plot object ." ,
128+ text : "Secondary axes , custom ticks, numeric formats, linked axes, and log/time scale controls ." ,
89129 code : 'plot.setSecondaryAxes({ y2: true });\nplot.setAxisScale({ x: "linear", y: "log" });\nplot.setAxisTicks("x1", ticks, { labels });' ,
90130 } ,
91131 {
92132 id : "subplots-plot" ,
93133 section : "Layout" ,
94134 title : "Linked Subplots + Crosshair" ,
95- text : "A 2x2 ImPlot subplot grid with shared x-axis interaction, linked crosshair, and mixed plot primitives ." ,
135+ text : "A 2x2 ImPlot subplot grid with linked x-axis behavior and crosshair synchronization ." ,
96136 code : 'plot.setSubplots(2, 2, { linkAllX: true });\nplot.setLinkedCrosshair("desk", { axis: "x" });\nplot.line("a", y, { x, subplotIndex: 0 });' ,
97137 } ,
98138 {
99139 id : "drag-plot" ,
100140 section : "Interaction" ,
101141 title : "Drag Lines, Drag Point, Drag Rect, Drag/Drop Targets" ,
102- text : "Interactive ImPlot primitives. Drag the vertical/horizontal guides, point, and rectangle; inspect interaction values below ." ,
142+ text : "Interactive ImPlot primitives for draggable guides, anchors, rectangles, and drop targets ." ,
103143 code : 'plot.dragLineX("cursor", 40);\nplot.dragPoint("anchor", 25, 0.5);\nplot.onInteraction(events => ...);' ,
104144 } ,
105145 {
106146 id : "colormap-plot" ,
107147 section : "Colormaps" ,
108148 title : "Colormap Widgets + Runtime Switching" ,
109- text : "Use the selector , slider, and buttons to verify that heatmaps and colorbar widgets use the active ImPlot colormap ." ,
149+ text : "Selector , slider, and color button widgets that update heatmaps and colorbar primitives at runtime ." ,
110150 code : 'plot.setColormap("Plasma");\nplot.colormapSelector({ label: "Choose map" });\nplot.colormapSlider({ label: "Sample" });' ,
111151 } ,
112152 {
113153 id : "advanced-api-plot" ,
114154 section : "Advanced API" ,
115155 title : "State, Selection, Export" ,
116- text : "A focused API coverage example for view/perf callbacks, state snapshots, PNG export, selection CSV, highlighting, constraints, links, and direct primitive access." ,
156+ text : "View callbacks, state snapshots, PNG export, selection CSV, highlighting, constraints, links, and direct primitive access." ,
117157 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");' ,
118158 } ,
119159] ;
@@ -129,88 +169,127 @@ export default function Page() {
129169 < span > nbimplot</ span >
130170 </ a >
131171 < div className = "topbar-links" >
172+ < a href = "#why" > Architecture</ a >
173+ < a href = "#command-center" > Demo Controls</ a >
174+ < a href = "#examples" > Examples</ a >
132175 < a href = "https://pypi.org/project/nbimplot/" > PyPI</ a >
133176 < a href = "https://www.npmjs.com/package/@nbimplot/web" > npm</ a >
134- < a href = "https://github.com/Prinkesh/nbimplot" > GitHub</ a >
135- < a className = "topbar-cta" href = "#command-center" > Try APIs</ a >
177+ < a className = "topbar-cta" href = "https://github.com/Prinkesh/nbimplot" > GitHub</ a >
136178 </ div >
137179 </ nav >
138180
139181 < section className = "hero-panel" >
140182 < div className = "hero-copy-block" >
141- < div className = "hero-badge-row" aria-label = "Project qualities" >
142- { heroBadges . map ( ( badge ) => (
143- < span key = { badge } > { badge } </ span >
144- ) ) }
145- </ div >
146- < p className = "eyebrow" > ImPlot quality, notebook reach</ p >
147- < h1 > Fast plotting for < span > million-point</ span > workflows.</ h1 >
183+ < p className = "eyebrow" > Jupyter-native plotting, powered by ImPlot</ p >
184+ < h1 > WASM plots for serious notebook data.</ h1 >
148185 < p className = "hero-copy" >
149- nbimplot brings an ImGui + ImPlot WASM core to notebooks and browser apps:
150- binary typed-array uploads, screen-resolution LOD, native pan/zoom, context
151- menus, subplots, colormaps, streaming updates, and exact selection APIs .
186+ nbimplot gives notebooks and browser apps the same interaction model: binary
187+ arrays into a strict ImGui + ImPlot WASM core, rendered on a canvas with
188+ pixel-bounded LOD for large time series .
152189 </ p >
153190 < div className = "hero-actions" >
154- < a className = "primary-link" href = "#examples" > Explore Examples </ a >
155- < a className = "secondary-link" href = "https://github.com/Prinkesh/nbimplot" > View Source </ a >
191+ < a className = "primary-link" href = "#developer-api" > Start Building </ a >
192+ < a className = "secondary-link" href = "#examples" > Explore Examples </ a >
156193 </ div >
157- < div className = "hero-proof-grid" aria-label = "Performance proof points" >
158- { heroProofs . map ( ( [ value , label ] ) => (
159- < div key = { label } >
160- < strong > { value } </ strong >
161- < span > { label } </ span >
162- </ div >
194+ < div className = "hero-promise-grid" aria-label = "Product constraints" >
195+ { productPromises . map ( ( promise ) => (
196+ < span key = { promise } > { promise } </ span >
163197 ) ) }
164198 </ div >
165199 </ div >
166200
167201 < div className = "hero-live-card" aria-label = "Live nbimplot canvas" >
168202 < div className = "hero-live-header" >
169203 < div >
170- < span > Live Surface</ span >
171- < strong > ImPlot WASM canvas</ strong >
204+ < span > Live WASM Surface</ span >
205+ < strong > ImPlot canvas inside the page </ strong >
172206 </ div >
173207 < code > drag pan / wheel zoom / double-click fit</ code >
174208 </ div >
175209 < div className = "hero-live-frame" >
176210 < div id = "hero-showcase-plot" className = "plot-host hero-plot-host" />
177211 </ div >
178- < div className = "hero-live-footer" >
179- < span > Binary data path</ span >
180- < span > LOD by default</ span >
181- < span > No native window</ span >
212+ </ div >
213+ </ section >
214+
215+ < section id = "developer-api" className = "developer-section" aria-label = "Notebook and web API" >
216+ < div className = "section-copy developer-copy" >
217+ < p className = "eyebrow" > Minimal API, no global state</ p >
218+ < h2 > One plotting model for notebooks and web apps.</ h2 >
219+ < p >
220+ The public surface is intentionally small: create a plot, attach typed
221+ arrays, update handles in place, and let the WASM core manage interaction,
222+ state, LOD, subplots, and export.
223+ </ p >
224+ </ div >
225+ < div className = "quickstart-grid" >
226+ < div className = "quickstart-card" aria-label = "Notebook quickstart" >
227+ < div className = "quickstart-top" >
228+ < span > Python</ span >
229+ < strong > notebooks</ strong >
230+ </ div >
231+ < pre > < code > { quickstart . join ( "\n" ) } </ code > </ pre >
232+ </ div >
233+ < div className = "quickstart-card quickstart-card-alt" aria-label = "Web quickstart" >
234+ < div className = "quickstart-top" >
235+ < span > TypeScript</ span >
236+ < strong > web apps</ strong >
237+ </ div >
238+ < pre > < code > { webstart . join ( "\n" ) } </ code > </ pre >
182239 </ div >
183240 </ div >
184241 </ section >
185242
186- < section className = "metrics " aria-live = "polite ">
187- { stats . map ( ( [ label , value ] ) => (
243+ < section className = "proof-strip " aria-label = "Performance proof points ">
244+ { proofPoints . map ( ( [ value , label ] ) => (
188245 < div key = { label } >
246+ < strong > { value } </ strong >
189247 < span > { label } </ span >
248+ </ div >
249+ ) ) }
250+ { stats . map ( ( [ label , value ] ) => (
251+ < div key = { label } >
190252 < strong > { value } </ strong >
253+ < span > { label } </ span >
191254 </ div >
192255 ) ) }
193256 < div >
194- < span > Mode</ span >
195257 < strong id = "mode" > initializing</ strong >
258+ < span > runtime status</ span >
196259 </ div >
197260 < div >
198- < span > Gallery</ span >
199- < strong > { examples . length } lazy plots</ strong >
200- </ div >
201- < div >
202- < span > Last frame</ span >
203261 < strong id = "frame-ms" > -- ms</ strong >
262+ < span > last frame</ span >
263+ </ div >
264+ </ section >
265+
266+ < section id = "why" className = "principles-section" >
267+ < div className = "section-copy" >
268+ < p className = "eyebrow" > Why this architecture</ p >
269+ < h2 > Designed around the expensive parts of plotting.</ h2 >
270+ < p >
271+ The Python layer validates and transfers buffers. The browser view owns the
272+ canvas lifecycle. The WASM core owns plot state, LOD, and ImPlot rendering.
273+ </ p >
274+ </ div >
275+ < div className = "principle-grid" >
276+ { principles . map ( ( item , index ) => (
277+ < article key = { item . title } className = "principle-card" >
278+ < span > { String ( index + 1 ) . padStart ( 2 , "0" ) } </ span >
279+ < h3 > { item . title } </ h3 >
280+ < p > { item . text } </ p >
281+ </ article >
282+ ) ) }
204283 </ div >
205284 </ section >
206285
207286 < section id = "command-center" className = "command-center" aria-label = "Interactive API command center" >
208- < div className = "command-copy" >
209- < p className = "eyebrow" > Command Center </ p >
210- < h2 > Drive real WASM plots from the page .</ h2 >
287+ < div className = "section-copy command-copy" >
288+ < p className = "eyebrow" > Live API surface </ p >
289+ < h2 > Operate the examples from one control panel .</ h2 >
211290 < p >
212- These controls load the target canvas, scroll it into view, and call the same
213- public APIs available from notebook widgets and web apps.
291+ These buttons load the target canvas, scroll it into view, and call the same
292+ public APIs available in notebooks and web apps.
214293 </ p >
215294 < p id = "feature-status" className = "feature-status" >
216295 Pick an action. The app will load the matching example and report the result here.
@@ -220,8 +299,8 @@ export default function Page() {
220299 < div className = "command-stack" >
221300 < section className = "control-panel" aria-label = "Global demo controls" >
222301 < div className = "control-copy" >
223- < span > Live Controls </ span >
224- < strong > Operate loaded plots</ strong >
302+ < span > Global controls </ span >
303+ < strong > Loaded plots</ strong >
225304 </ div >
226305 < div className = "toolbar" >
227306 < button id = "update-data" type = "button" data-label = "data" > Update Data</ button >
@@ -262,21 +341,22 @@ export default function Page() {
262341
263342 < section className = "info-grid" aria-label = "Usage notes and documentation" >
264343 < section className = "notes-panel" >
265- < strong > Interaction checklist:</ strong > examples lazy-load as they approach the
266- viewport and offscreen canvases are released to keep WebGL contexts bounded.
267- Once loaded, left-drag pans, wheel zooms, scroll over axes zooms that axis,
268- right-click opens ImPlot menus, right-drag box-select/box-zoom follows ImPlot
269- behavior, and double-click autofits.
344+ < p className = "eyebrow" > Interaction checklist</ p >
345+ < p >
346+ Examples lazy-load near the viewport and offscreen canvases are released to
347+ keep WebGL contexts bounded. Once loaded, left-drag pans, wheel zooms,
348+ right-click opens ImPlot menus, right-drag box-select/box-zoom follows
349+ ImPlot behavior, and double-click autofits.
350+ </ p >
270351 </ section >
271352
272353 < section className = "resource-panel" aria-label = "Documentation and AI-readable resources" >
273354 < div >
274355 < p className = "eyebrow" > Documentation</ p >
275- < h2 > Human-readable and agent-readable guides .</ h2 >
356+ < h2 > Guides for users, agents, and web integrations .</ h2 >
276357 < p >
277- These links make the project easier to classify for users, search engines,
278- coding assistants, and retrieval-augmented agents looking for fast notebook
279- plotting, ImPlot in Jupyter, WASM plotting, and large-data visualization.
358+ Direct resources for fast notebook plotting, million-point visualization,
359+ web app integration, and LLM/search positioning.
280360 </ p >
281361 </ div >
282362 < div className = "resource-links" >
@@ -288,12 +368,14 @@ export default function Page() {
288368 </ section >
289369
290370 < section id = "examples" className = "examples-heading" >
291- < p className = "eyebrow" > Examples Gallery</ p >
292- < h2 > Every supported plot primitive, grouped like release documentation.</ h2 >
293- < p >
294- Scroll through lazy-loaded canvases to verify independent WASM sessions,
295- lifecycle cleanup, interaction primitives, and colormap propagation.
296- </ p >
371+ < div className = "section-copy" >
372+ < p className = "eyebrow" > Examples gallery</ p >
373+ < h2 > Coverage across ImPlot-style primitives.</ h2 >
374+ < p >
375+ Each canvas is an independent WASM session. Scroll to lazy-load examples and
376+ verify lifecycle cleanup, interactions, subplots, colormaps, and exports.
377+ </ p >
378+ </ div >
297379 < div className = "example-tabs" aria-label = "Example categories" >
298380 { exampleSections . map ( ( section ) => (
299381 < span key = { section } > { section } </ span >
0 commit comments