Professional Svelte component to explore huge time-series datasets directly in the browser using DuckDB-WASM, Apache Arrow, and SVECharts.
@qtsurfer/svelte-timeseries ships everything you need to build financial, industrial, or scientific dashboards with millions of data points. The component offers:
| Layer | Role |
|---|---|
| DuckDB-WASM | Runs SQL against Parquet without any backend and keeps data in columnar memory. |
TimeSeriesFacade |
Coordinates DuckDB + TimeSeriesChartBuilder, handles incremental column loads, and exposes UI state. |
@qtsurfer/sveltecharts |
Facade that builds and updates ECharts instances declaratively. |
| SvelteKit | Hosts the component, snippets, and demo routes. |
pnpm add @qtsurfer/svelte-timeseries
# or
npm install @qtsurfer/svelte-timeseries
yarn add @qtsurfer/svelte-timeseries
Requirements:
<script lang="ts">
import { SvelteTimeSeries } from '@qtsurfer/svelte-timeseries';
const tables = {
temps: {
url: '/temps_gzip.parquet',
mainColumn: 'temp'
}
};
const markers = {
table: 'temps',
targetColumn: '_signal',
targetDimension: 'temp'
};
</script>
<SvelteTimeSeries table={tables} {markers} debug={false} />
| Prop | Type | Description |
|---|---|---|
table |
Record<string, { url: string; mainColumn: string; columnsSelect?: string[] }> |
Defines the Parquet sources and their primary column; the object key becomes the DuckDB view name. |
markers? |
MarkersTableOptions |
Table and JSON column used to build the markers view (shape, color, position, text). |
debug? |
boolean (default true) |
Enables verbose DuckDB/builder logging. |
columnsSnippet? |
Snippet<[ColumnsProps]> |
Overrides the column toggle panel. |
performanceSnippet? |
Snippet<[PerformanceProps]> |
Overrides the performance/metrics panel. |
TimeSeriesFacade (see src/lib/TimeSeriesFacade.ts) encapsulates the component logic:
initialize) – downloads the primary column, builds the dataset, and configures legends/icons.addDimension / toggleColumn) – fetches new columns only when requested.loadMarkers) – reads the markers view and adds annotations to ECharts.getColumns, describe, getLegendStatus) – provides data for custom panels without touching DuckDB again.Import the class directly to craft bespoke dashboards while reusing the DuckDB → Arrow → ECharts pipeline.
Reuse the same instance to run bespoke SQL before/after chart rendering.
import { DuckDB } from '@qtsurfer/svelte-timeseries';
const duck = await DuckDB.create(
{
signal: {
url: '/signals.parquet',
mainColumn: 'price'
}
},
undefined,
true
);
const rows = await duck.getRangeData('signal', '2024-01-01', '2024-01-31', 1000);
await duck.closeConnection();
Key implementations (src/lib/duckdb/DuckDB.ts):
DuckDB.create validates window + Worker, registers Parquet views, and reports load time.getSingleDimension normalizes timestamps (ms) and returns Arrow arrays ready for TimeSeriesChartBuilder.buildTablesAndSchemas auto-detects types (casts % columns to DOUBLE, skips helper fields, builds the markers view).transformTableToMatrix converts Arrow results into [rows, columns] matrices consumable by any UI.Craft fully custom ECharts layouts while reusing legend, marker, and metrics logic.
import { TimeSeriesChartBuilder } from '@qtsurfer/svelte-timeseries';
const builder = new TimeSeriesChartBuilder(echartsInstance, {
externalManagerLegend: true
});
builder.setLegendIcon('circle');
builder.setDataset(
{ _ts: timestamps, price: prices, ema20: ema20Series },
['_ts', 'price', 'ema20']
);
builder.addMarkerPoint(
{ dimName: 'price', timestamp: timestamps[100], name: 'Breakout' },
{ icon: 'pin', color: '#FF7F50' }
);
builder.build();
Taken from the demo at packages/svelte-timeseries/src/routes/+page.svelte:
temps_gzip_mini.parquet): ideal for embedded dashboards or smoke tests.temps_gzip.parquet): browser stress test without compromising UX.columnsSelect to keep the initial payload slim and load indicators on demand._m signals on top of price.pnpm install
pnpm dev --filter svelte-timeseries
packages/svelte-timeseries/static. Adjust the demo baseUrl when publishing behind a CDN.DuckDB.ts: closeConnection, getRangeData, getMarkers.debug={true} to measure real load times per browser.