Perspective: A WASM-Powered Telescope for Your Data
Some datasets refuse to sit still. They stream in from market feeds, IoT sensors, or live user activity, and they demand to be explored, pivoted, and charted right now. @finos/perspective is a library built for exactly this scenario: a high-performance analytics engine compiled from C++ to WebAssembly, packaged as a framework-agnostic Web Component that handles millions of rows without breaking a sweat.
Originally developed under the FINOS (Fintech Open Source Foundation) umbrella and now part of the OpenJS Foundation as of v4.0.0, Perspective gives you an interactive data grid, over ten chart types, real-time streaming support, and native Apache Arrow compatibility -- all running inside the browser. Whether you are building a financial trading dashboard, a log explorer, or a business intelligence tool, Perspective turns raw data into interactive reports with minimal effort.
What Makes It Tick
Perspective packs a serious feature set into its Web Component shell:
- WebAssembly query engine: The core is written in C++ and compiled to WASM, enabling in-browser analytics on datasets that would choke a pure JavaScript solution.
- 10+ visualization types: Line, bar, area, scatter, heatmap, treemap, sunburst, candlestick charts and a full-featured data grid with sorting, filtering, and grouping.
- Streaming-first architecture: Tables accept real-time updates, making it natural to pipe in WebSocket feeds or polling data.
- Apache Arrow native: Reads and writes Arrow format natively, which means zero-copy data interchange with other Arrow-compatible tools.
- Expression language: A columnar expression language based on ExprTK lets you create computed columns without touching your source data.
- Pluggable backends: Virtual server API supports DuckDB, ClickHouse, and custom backends so you can query remote data sources through the same interface.
- Multi-language support: The same engine runs in JavaScript (browser and Node.js), Python, and Rust.
Getting Started
Install the legacy FINOS packages (v3.x) or the newer OpenJS packages (v4.x). For the FINOS namespace:
npm install @finos/perspective @finos/perspective-viewer @finos/perspective-viewer-datagrid @finos/perspective-viewer-d3fc
yarn add @finos/perspective @finos/perspective-viewer @finos/perspective-viewer-datagrid @finos/perspective-viewer-d3fc
If you are starting fresh, the v4.x packages live under @perspective-dev/* with the same structure.
Your First Dashboard in Five Minutes
Loading Data Into a Table
The core workflow starts with creating a worker, loading data into a table, and handing that table to the viewer component.
import perspective from "@finos/perspective";
import "@finos/perspective-viewer";
import "@finos/perspective-viewer-datagrid";
import "@finos/perspective-viewer-d3fc";
const worker = await perspective.worker();
const table = await worker.table({
timestamp: [new Date("2026-01-01"), new Date("2026-01-02"), new Date("2026-01-03")],
product: ["Widget A", "Widget B", "Widget A"],
revenue: [1200, 3400, 2800],
units: [10, 25, 18],
});
const viewer = document.getElementById("viewer") as HTMLElement & { load: (t: any) => Promise<void> };
await viewer.load(table);
<perspective-viewer id="viewer" style="width: 100%; height: 600px;"></perspective-viewer>
That is all it takes. The viewer renders a fully interactive data grid with column sorting, filtering controls, and the ability to switch to any chart type from the toolbar.
Configuring Views Programmatically
You can set up the viewer with a specific configuration instead of relying on user interaction.
const viewer = document.getElementById("viewer") as HTMLElement & {
load: (t: any) => Promise<void>;
restore: (config: Record<string, unknown>) => Promise<void>;
};
await viewer.load(table);
await viewer.restore({
plugin: "Y Bar",
group_by: ["product"],
columns: ["revenue"],
aggregates: { revenue: "sum" },
});
This renders a bar chart grouped by product with summed revenue, no clicks required.
Filtering and Sorting
The viewer configuration also accepts filters and sort directives.
await viewer.restore({
plugin: "Datagrid",
sort: [["revenue", "desc"]],
filter: [["units", ">", 12]],
columns: ["product", "revenue", "units"],
});
Streaming and Expressions
Real-Time Data Updates
One of Perspective's strongest suits is handling streaming data. Once you have a table, you can push updates to it and the viewer reflects changes automatically.
const table = await worker.table({
symbol: "string",
price: "float",
volume: "integer",
timestamp: "datetime",
});
const viewer = document.getElementById("viewer") as HTMLElement & { load: (t: any) => Promise<void> };
await viewer.load(table);
function simulateMarketFeed(): void {
setInterval(() => {
table.update([
{
symbol: "AAPL",
price: 180 + Math.random() * 10,
volume: Math.floor(Math.random() * 100000),
timestamp: new Date(),
},
{
symbol: "GOOGL",
price: 140 + Math.random() * 8,
volume: Math.floor(Math.random() * 80000),
timestamp: new Date(),
},
]);
}, 1000);
}
simulateMarketFeed();
The table schema is declared with type strings, and subsequent update calls append or replace rows. The viewer automatically re-renders as data flows in.
Computed Columns with Expressions
The expression language lets you derive new columns on the fly without modifying the underlying data.
await viewer.restore({
plugin: "Datagrid",
columns: ["symbol", "price", "volume", "notional"],
expressions: {
notional: '"price" * "volume"',
},
});
Expressions reference column names in double quotes and support arithmetic, string functions, conditionals, and more. This is particularly useful for financial calculations where derived metrics are the norm.
Connecting to Remote Data Sources
Perspective supports a client-server architecture where the heavy computation happens on a backend while the viewer runs in the browser.
import perspective from "@finos/perspective";
const client = await perspective.websocket("ws://localhost:8080");
const serverTable = await client.open_table("stock_data");
const viewer = document.getElementById("viewer") as HTMLElement & { load: (t: any) => Promise<void> };
await viewer.load(serverTable);
The WebSocket connection lets you query datasets that are too large to fit in browser memory. The server can be a Node.js process, a Python server using perspective-python, or a Rust backend.
Wrapping Up
Perspective occupies a unique space in the data visualization landscape. While libraries like AG Grid focus purely on grids and D3.js requires you to build everything from scratch, Perspective delivers a complete analytics toolkit -- query engine, grid, charts, streaming, and expressions -- in a single Web Component. The WebAssembly core means performance scales to millions of rows without sending data to a server, and the pluggable architecture means you can connect to DuckDB or ClickHouse when your dataset outgrows the browser.
The trade-off is a larger bundle size (the WASM binaries add up) and a steeper learning curve compared to simpler table libraries. But if your use case involves real-time data, complex aggregations, or interactive exploration of large datasets, @finos/perspective is one of the most capable tools available. The transition from FINOS to the OpenJS Foundation signals a healthy, well-governed future for the project.