Skip to content

Commit 64d6043

Browse files
author
root
committed
Refine demo product design
1 parent ff41feb commit 64d6043

2 files changed

Lines changed: 561 additions & 599 deletions

File tree

app/page.jsx

Lines changed: 156 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1858
const 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

Comments
 (0)