npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

arrow-cluster-layer

v0.3.3

Published

deck.gl CompositeLayer for clustered point rendering from Apache Arrow tables

Readme

arrow-cluster-layer

npm version npm downloads bundle size license

A deck.gl CompositeLayer for rendering clustered point data from Apache Arrow tables. Built on top of arrow-supercluster.

Live Demo — cluster 200K / 1M / 2M points from GeoParquet in the browser.

Why

Replaces the typical GeoJSON → Supercluster → GeoJsonLayer pipeline with Arrow Table → ArrowClusterEngine → binary ScatterplotLayer. No intermediate JS objects, no GeoJSON serialization. Typed arrays go straight to the GPU.

Install

# pnpm
pnpm add arrow-cluster-layer @deck.gl/core @deck.gl/layers apache-arrow

# npm
npm install arrow-cluster-layer @deck.gl/core @deck.gl/layers apache-arrow

# yarn
yarn add arrow-cluster-layer @deck.gl/core @deck.gl/layers apache-arrow

@deck.gl/core, @deck.gl/layers, and apache-arrow are peer dependencies.

arrow-supercluster is included as a direct dependency — you don't need to install it separately. Its types are re-exported for convenience.

Usage

import { ArrowClusterLayer } from "arrow-cluster-layer";
import type { Table } from "apache-arrow";

// Load your Arrow Table however you like (GeoParquet, IPC, etc.)
const table: Table = /* ... */;

const layer = new ArrowClusterLayer({
  id: "clusters",
  data: table,
  geometryColumn: "geometry",
  idColumn: "id",

  // Clustering
  clusterRadius: 75,
  clusterMaxZoom: 16,
  clusterMinZoom: 0,
  clusterMinPoints: 2,

  // Styling
  primaryColor: [26, 26, 64, 200],
  secondaryColor: [100, 100, 200, 200],
  selectedColor: [255, 140, 0, 230],
  textOpacity: 255,
  pointRadiusMinPixels: 10,
  pointRadiusMaxPixels: 100,

  // Interaction state
  selectedClusterId: null,
  focusedClusterId: null,

  // View
  viewType: "map", // or "globe"

  pickable: true,
});

Props

Data

| Prop | Type | Default | Description | | ---------------- | ------------- | ------------ | ----------------------------------------------------------------------- | | data | arrow.Table | required | Arrow Table with a GeoArrow Point geometry column | | geometryColumn | string | "geometry" | Name of the geometry column | | idColumn | string | "id" | Reserved for future use (currently ignored — IDs are Arrow row indices) |

Clustering

| Prop | Type | Default | Description | | ------------------ | -------- | ------- | ----------------------------- | | clusterRadius | number | 75 | Cluster radius in pixels | | clusterMaxZoom | number | 20 | Max zoom level for clustering | | clusterMinZoom | number | 2 | Min zoom level for clustering | | clusterMinPoints | number | 2 | Min points to form a cluster |

Styling

| Prop | Type | Default | Description | | ---------------------- | ----------- | ---------------------- | ----------------------------------- | | primaryColor | ColorRGBA | [26, 26, 64, 200] | Default fill color | | secondaryColor | ColorRGBA | [100, 100, 200, 200] | Focused cluster + descendants color | | selectedColor | ColorRGBA | [255, 140, 0, 230] | Selected cluster color | | textOpacity | number | 255 | Cluster label text opacity (0-255) | | pointRadiusMinPixels | number | 10 | Minimum circle radius in pixels | | pointRadiusMaxPixels | number | 100 | Maximum circle radius in pixels |

Interaction

| Prop | Type | Default | Description | | ------------------- | ------------------ | ------- | ------------------------------------------- | | selectedClusterId | number \| null | null | Currently selected cluster ID | | focusedClusterId | number \| null | null | Currently focused (hovered) cluster ID | | viewType | "map" \| "globe" | "map" | Flips text labels on globe view at low zoom |

Filtering

| Prop | Type | Default | Description | | ------------ | -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | filterMask | Uint8Array \| null | null | Boolean mask for filtering which points enter the cluster index. Length must equal table.numRows. 0 = excluded, non-zero = included. When null, all points are included. |

Picking

The layer returns ArrowClusterPickingInfo from onHover and onClick:

interface ArrowClusterPickingInfo extends PickingInfo {
  isCluster: boolean; // cluster or individual point?
  clusterId: number; // encoded cluster ID or Arrow row index
  pointCount: number; // points in cluster (1 for individual)
  arrowIndices: number[]; // Arrow row indices of all leaf points
  rows: StructRowProxy[]; // materialized Arrow rows
}

Example:

onClick: (info) => {
  const pick = info as ArrowClusterPickingInfo;
  if (pick.isCluster) {
    // Zoom into the cluster
    const zoom = layer.getClusterExpansionZoom(pick.clusterId);
    // ... fly to pick.coordinate at zoom
  } else {
    // Individual point — access the Arrow row
    const row = pick.rows[0];
    console.log(row.id, row.city, row.description);
  }
};

Public Methods

layer.getClusterExpansionZoom(clusterId) → number

Returns the zoom level at which a cluster splits into its children. Useful for zoom-on-click behavior.

Rendering

The layer renders two sublayers:

  • ScatterplotLayer — circles with binary attribute buffers (positions, radii, colors as typed arrays)
  • TextLayer — cluster count labels with SDF fonts

Radius scales logarithmically with point count: baseSize + (log(count+1) / log(total+1)) * scaleFactor.

Text color is automatically chosen for contrast (black or white) based on the fill color's relative luminance.

Style Helpers

The style computation functions are exported if you need to customize or extend them:

import {
  computeFillColors,
  computeRadii,
  computeTextColors,
  computeTexts,
} from "arrow-cluster-layer";

Re-exports

The engine and its types are re-exported for convenience:

import {
  ArrowClusterEngine,
  type ClusterOutput,
  type ArrowClusterEngineOptions,
} from "arrow-cluster-layer";

License

ISC