Skip to main content

LuxFilter: GPU-Resident Crossfiltering

@luma.gl/experimental/luxfilter coordinates linked selections across GPU-resident maps, scatterplots, histograms, categorical summaries, and application-defined views. Brush one view, update its selection, and encode the same command graph again: source rows, selection masks, aggregations, and stable visible-row identifiers remain on the GPU.

LuxFilter is an experimental, renderer-independent WebGPU controller. It does not ship a dashboard framework, dataframe importer, or charting library; applications retain ownership of their source vectors, output buffers, user interface, rendering, command submission, and optional readback.

Try the million-row explorer

The embedded Million-Row Crossfilter Explorer links a GPU-rendered map, scatterplot, three self-excluding histograms, and categorical cohorts across 1,048,576 synthetic rows. Start it when you are ready to allocate a WebGPU device; ordinary visits to this documentation do not import the example registry or initialize the dashboard.

Optional interactive WebGPU explorerExplore one million linked GPU-resident rows.Brush a map, scatterplot, or histogram and watch every linked view update together.

Brush the map or scatterplot, adjust a histogram range, and try the preset scenarios. The source columns and linked masks stay on the GPU; the example explicitly reads only one compact summary buffer to update its visible counts and distributions.

Create linked selections and views

import {GPUCommandGraph} from '@luma.gl/experimental';
import {LuxFilter} from '@luma.gl/experimental/luxfilter';

const graph = new GPUCommandGraph(device);
const longitude = graph.importGPUVector('longitude', longitudeVector);
const latitude = graph.importGPUVector('latitude', latitudeVector);
const value = graph.importGPUVector('value', valueVector);
const category = graph.importGPUVector('category', categoryVector);

const filter = new LuxFilter(graph, {
id: 'linked-dashboard',
dimensions: [
{id: 'map', kind: 'bounds', x: longitude, y: latitude},
{id: 'value', kind: 'range', input: value},
{id: 'category', kind: 'range', input: category}
],
views: [
{
id: 'distribution',
kind: 'histogram',
dimension: 'value',
input: value,
domain: [0, 100],
output: histogramCounts
},
{id: 'cohorts', kind: 'group', keys: category, output: categoryCounts},
{
id: 'visible-rows',
kind: 'visibility',
output: visibleSourceIds,
count: visibleCount
}
],
outputMask: combinedSelectionMask
});

filter.addToGraph(graph);
const compiled = graph.compile();

filter.setBounds('map', [minimumLongitude, minimumLatitude, maximumLongitude, maximumLatitude]);
filter.setRange('value', [20, 40]);
filter.setRange('category', [categoryId, categoryId]);

const encoder = device.createCommandEncoder({id: 'linked-dashboard-update'});
compiled.encode(encoder, {parameters: undefined});
device.submit(encoder.finish());

range selects inclusive scalar endpoints. bounds selects an inclusive axis-aligned rectangle. All active dimensions intersect; exact categorical selection can be expressed as an identical minimum and maximum unsigned category identifier.

Histogram and group views exclude their own associated selection by default, retaining the available distribution while that view is brushed. Set includeOwnSelection: true when a view should include its own predicate. Visibility views publish stable compacted row identifiers and a visible count; mask views publish source-aligned selection flags for custom rendering or compute work.

Selections accept packed float32, sint32, and uint32 scalar inputs. Chunked GPUVector inputs retain their original ordered chunk boundaries; chunking is not distributed or multi-GPU execution. Call filter.clear(dimensionId), filter.clearAll(), or filter.destroy() as the application updates or releases its selections.

Attribution and feature comparison

LuxFilter is inspired by NVIDIA RAPIDS cuXfilter, whose contributors demonstrated how GPU-resident dataframes, coordinated visualizations, and linked selections can make large-scale exploratory analysis feel immediate. cuXfilter itself acknowledged the original JavaScript Crossfilter; LuxFilter brings that family of ideas back into the browser with modern WebGPU execution.

We gratefully acknowledge NVIDIA and the RAPIDS contributors for that pioneering work. cuXfilter is distributed under the Apache License 2.0. LuxFilter is an independently written, MIT-licensed vis.gl implementation; it does not copy or translate cuXfilter source code. It is not a CUDA port, compatible Python API, feature-parity claim, or NVIDIA/RAPIDS successor project, and is neither affiliated with nor endorsed by NVIDIA.

The comparison below reflects the official cuXfilter 26.06 documentation, DataFrame and dashboard API, chart integrations, multi-GPU guide, and RAPIDS sunset notice.

CapabilityNVIDIA RAPIDS cuXfilterluma.gl LuxFilter
Primary interfacePython cuxfilter.DataFrame and dashboard() in notebooks or Bokeh applicationsTypeScript LuxFilter controller inside a browser application
GPU platformCUDA, cuDF, and NVIDIA GPUsA browser-supported WebGPU adapter and luma.gl GPU command graphs
Source datacuDF or Dask-cuDF dataframes; Arrow files and tables through from_arrow()Caller-owned typed graph views and GPUVector chunks; no built-in dataframe, file, or Arrow importer
Linked interactionCoordinated chart and widget selections across dataframe dimensionsInclusive scalar ranges and rectangular brushes; active dimensions intersect
Input value typescuDF-supported numeric, string, and datetime columns, subject to chart supportPacked float32, sint32, and uint32; categorical keys use uint32
AggregationGPU dataframe filtering, grouping, and chart-specific aggregationGPU histograms plus dense grouped count, sum, minimum, maximum, and mean
Selection outputsFiltered cuDF exports and queried dataframe indicesGPU-resident masks, stable compacted source identifiers, and visible-row counts
Visualization ecosystemBokeh, Datashader, deck.gl, Panel widgets, and table integrationsRenderer-independent GPU outputs; applications provide charts, maps, and scatterplots
Dashboard compositionBuilt-in chart collections, widgets, layouts, themes, and notebook/server presentationFiltering primitives only; applications own layout, controls, rendering, and lifecycle
GPU data boundarycuDF data lives with the Python/CUDA application; charts consume their required resultsUploaded source rows, masks, and aggregates stay on the local GPU; compact display readback is explicit
Scaling modelSingle-GPU cuDF or distributed, multi-GPU Dask-cuDFOne WebGPU device; preserves source chunks without claiming multi-GPU execution
DeploymentJupyter notebooks, Bokeh-backed applications, and documented multi-user deploymentsA browser application; no Python process or dashboard server is required
Project statusFinal RAPIDS release 26.06; subsequently sunset and archivedExperimental optional @luma.gl/experimental/luxfilter entry point

The projects solve related interaction problems in different environments. LuxFilter does not provide cuXfilter feature parity, a migration layer, built-in chart integrations, or distributed GPU execution.

See GPU Primitives and Command Graphs for the underlying compute infrastructure and GPU Coordinate Projection for another browser-native GPU data workflow.