Skip to main content

Supported Arrow Types

From: v10Status: Work-In-Progress

This page documents how Apache Arrow columns map to luma.gl GPU upload, vertex attributes, WebGPU storage bindings, and model-specific preparation helpers. For the generic GPU table ownership model, see GPU Table Lifecycle.

Apache Arrow Preliminaries

Apache Arrow has a rich type system that can represent a wide variety of binary data columns. A subset of these column types can be used directly as GPU vertex attribute data, meaning that such arrow columns can be uploaded efficiently to the GPU.

Apache Arrow supports primitive types like Float32, Uint32, and Uint8 that describe the value stored in each row. It also supports fixed-length vectors of these types with FixedSizeList. These scalar and fixed-length vector types map directly to the memory layouts used by GPU vertex attributes.

Arrow also supports variable-length List columns. These are useful for data such as polygons and paths, but they do not map directly to a single vertex attribute without an additional conversion step. ArrowPathModel provides the attribute-backed conversion for prepared Float32 XY, XYZ, and XYZM path coordinate rows, expanding each logical path into segment instances while keeping row-level style columns at the path boundary. ArrowStoragePathModel provides the WebGPU storage-backed form: compute expands GPU-resident path values into compact indexed segment records from copied list-offset metadata, render shaders fetch coordinates from the original path-value storage buffer, and per-path style rows remain storage bindings instead of being repeated for every generated segment. Use ArrowPathModel.prepareGPUVectors() or prepareArrowPathGPUVectors() to turn raw Float32 or Float64 Arrow path vectors into prepared attribute-path inputs. Use ArrowStoragePathModel.prepareGPUVectors() or prepareArrowStoragePathGPUVectors() when WebGPU storage rendering should convert Float64 path payloads into Float32 deltas on the GPU before rendering.

Arrow Column Support

Support depends on how the column is consumed. A column can be uploadable as a GPUVector without also being usable as a generic vertex attribute.

Arrow adapters publish GPUVector.format strings from @luma.gl/tables. Arrow DataType values are retained only as adapter/readback metadata during migration.

Arrow column patternGPUVector formatGeneric BufferLayout
Float32float32yes
FixedSizeList<Float32, 3>float32x3yes
FixedSizeList<Uint8, 4> as normalized colorunorm8x4yes
List<FixedSizeList<Float32, 3>> path coordinatesvertex-list<float32x3>no
List<FixedSizeList<Uint8, 4>> vertex colorsvertex-list<unorm8x4>no

vertex-list<...> vectors keep source row boundaries through list-offset metadata owned by the producing adapter. They are not automatically exposed as ordinary vertex attributes; path, text, polygon, and geometry adapters must expand them or bind their flattened values explicitly.

Status: ✅ directly supported, 🟡 conditionally supported, ❌ not supported. The nullable column refers to columns with actual null rows, not just nullable schema fields with nullCount: 0.

Scalar-valued Columns

Arrow data typeGPU
upload
nullableUpload notesShader-facing
use
Higher-level
model support
Int8 | Uint8 | Int16 | Uint16 |
Int32 | Uint32 | Float16 | Float32
Scalar GPUVector or selected GPUTable columns.Scalar attributes or storage rows, subject to shader type and vertex format compatibility.Numeric table workflows and scalar style rows such as text angles, text sizes, and path widths.
Float64🟡Raw values can be copied into a GPUVector, but generic render paths should repack them to Float32, origin-relative Float32, or u32 word pairs at preparation boundaries.No generic f64 vertex attribute or WGSL scalar path; use prepared Float32 values or custom word-pair helpers.Matrix columns truncate to Float32; Float64 paths become per-row Float32 deltas plus retained origins.
Uint64 carrying DGGS cell keys🟡DGGS WebGPU helpers retain Uint64 rows as word-pair storage.Read through DGGS WGSL helpers or expand through prepareDggsCellPathGPUVector(); not a generic vertex attribute.Global grid cell keys parsed from UTF-8 geohash, quadkey, S2, A5, or H3 IDs.
BoolArrow Bool values are bit-packed, so they are not a directly uploadable scalar buffer. Repack to Uint8 or Uint32 when shaders need flags.Use as integer 0/1 flags after repacking; otherwise keep on CPU as row metadata.closeArrowPaths() accepts Vector<Bool> closed-path flags as CPU input; a future adapter could publish repacked flag columns.

Vector-valued Columns

Arrow data typeGPU
upload
nullableUpload notesShader-facing
use
Higher-level
model support
FixedSizeList<numeric,
1 | 2 | 3 | 4>
Fixed-width GPU rows.
Planned color adapter support will expand three-component RGB source rows to four-component RGBA rows. FixedSizeList<Float16, 4> is the compact scene-linear/HDR color form.
Vector attributes or storage rows; 3-component 8-bit and 16-bit integer formats are WebGL-only unless padded.Positions, point rows, normalized colors, scene-linear/HDR colors, pixel offsets, clip rectangles, mesh attributes, and path style rows.
List<FixedSizeList<Float32,
2 | 3 | 4>>
Flattened GPU path-coordinate values plus copied list-offset metadata.Not a single generic vertex attribute.
ArrowPathModel expands to segment attributes; ArrowStoragePathModel keeps values in storage.
ArrowPathModel.prepareGPUVectors() uploads unchanged unless closed-path normalization is requested.
Outputs feed ArrowPathModel and ArrowStoragePathModel.
List<FixedSizeList<Float64,
2 | 3 | 4>>
🟡Path preparation emits per-row Float32 deltas plus copied list-offset metadata.
CPU Float64 per-row origins are retained separately.
Not a generic vertex attribute.
Attribute paths prepare deltas on CPU; WebGPU storage paths can prepare them on GPU before rendering.
ArrowPathModel.prepareGPUVectors() and prepareArrowPathGPUVectors() prepare attribute paths.
ArrowStoragePathModel.prepareGPUVectors() prepares storage paths.
List<numeric |
FixedSizeList<numeric, 1 | 2 | 3 | 4>>
Flattened GPU values plus copied list-offset metadata.Not a single generic vertex attribute; consume through storage/compute code or model-specific expansion.Generic variable-length numeric storage/readback. Model support is documented by the model-specific rows above.

The Points example demonstrates FixedSizeList<Float32, 2 | 3 | 4> and DenseUnion point rows, including M-coordinate or timestamp animation and hover identity labels. The Lines example demonstrates DenseUnion line rows and polygon outlines by normalizing them into prepared line rows before the path models consume them. The GeoArrow example routes a mixed DenseUnion geometry column through point, line, and polygon renderers.

Matrix-valued Columns

Arrow data typeGPU
upload
nullableUpload notesShader-facing
use
Higher-level
model support
FixedSizeList<Float32 | Float64, 4>
with visgl: mat2x2 metadata
🟡Through prepareArrowMatrixGPUVector().
Canonical output is Float32 column-major wgsl-storage; Float64 truncates to Float32.
mat2x2<f32> storage rows or two lowered matrix-column attributes.Compact 2D linear transforms.
FixedSizeList<Float32 | Float64, 6>
with visgl: mat2x3 metadata
🟡Through prepareArrowMatrixGPUVector().
Packed 6-value sources normalize to 8-value Float32 WGSL-storage rows.
mat2x3<f32> storage rows or two lowered matrix-column attributes with WGSL three-row padding.2-column affine-style transforms with three row components.
FixedSizeList<Float32 | Float64, 6>
with visgl: mat3x2 metadata
🟡Through prepareArrowMatrixGPUVector().
Canonical output is Float32 column-major wgsl-storage; Float64 truncates to Float32.
mat3x2<f32> storage rows or three lowered matrix-column attributes.Three-column transforms with two row components.
FixedSizeList<Float32 | Float64, 9>
with visgl: mat3x3 metadata
🟡Through prepareArrowMatrixGPUVector().
Packed 9-value sources normalize to 12-value Float32 WGSL-storage rows.
mat3x3<f32> storage rows or three lowered matrix-column attributes with WGSL three-row padding.2D/3D linear transforms and rotation/scale bases.
FixedSizeList<Float32 | Float64, 12>
with visgl: mat4x3 metadata
🟡Through prepareArrowMatrixGPUVector().
Packed 12-value sources normalize to 16-value Float32 WGSL-storage rows.
mat4x3<f32> storage rows or four lowered matrix-column attributes with WGSL three-row padding.deck.gl SimpleMeshLayer and ScenegraphLayer use this affine GPU payload: three model-matrix vec3 columns plus one translation vec3.
FixedSizeList<Float32 | Float64, 12>
with visgl: mat3x4 metadata
🟡Through prepareArrowMatrixGPUVector().
Canonical output is Float32 column-major wgsl-storage; Float64 truncates to Float32.
mat3x4<f32> storage rows or three lowered matrix-column attributes.Three-column transforms with four row components.
FixedSizeList<Float32 | Float64, 16>
with visgl: mat4x4 metadata
🟡Through prepareArrowMatrixGPUVector().
Canonical output is Float32 column-major wgsl-storage; Float64 truncates to Float32.
mat4x4<f32> storage rows or four lowered matrix-column attributes.Full homogeneous 3D transforms.

Time-valued Columns

Arrow data typeGPU
upload
nullableUpload notesShader-facing
use
Higher-level
model support
Date | Time | Timestamp | Duration🟡Through prepareArrowTemporalGPUVector().
Emits relative Float32 values plus persisted temporal origin metadata.
Scalar relative-time attributes or storage rows in the source Arrow unit.Animated or filtered rows; see Time Columns and Blinking Stars.
List<Date | Time |
Timestamp | Duration>
🟡Through prepareArrowTemporalGPUVector().
Preserves list offsets while emitting relative List<Float32> values.
Not a single generic vertex attribute; consume through storage/compute code or model-specific expansion.Trips-style per-path timestamp streams aligned with path vertices, including ArrowStorageTripsPathModel.
IntervalInterval is compound calendar/time data, not one scalar time value.
There is no single preserved unit or origin subtraction rule for prepareArrowTemporalGPUVector().
Not supported as a generic scalar temporal attribute or storage row.Deferred; callers should lower intervals into application-defined scalar columns.
List<Interval>The leaf interval has unresolved compound/calendar semantics, and the list form needs a model-specific variable-length consumption path.Not supported as generic temporal storage or Trips-style timestamps.Deferred; Trips-style streams require scalar Date, Time, Timestamp, or Duration leaves.

Text-valued Columns

Arrow data typeGPU
upload
nullableUpload notesShader-facing
use
Higher-level
model support
Utf8🟡🟡UTF-8 value bytes plus readback metadata; null bitmaps are retained for readback, not generic shader use.Not a generic vertex attribute; consume through text-specific UTF-8 and glyph expansion, or parse DGGS cell keys through prepareDggsCellKeyGPUVector() when the encoding is supplied.Arrow text conversion helpers, pure text models, and DGGS key parsing.
Dictionary<Utf8, Int8 | Int16 | Int32 |
Uint8 | Uint16 | Uint32>
🟡Dictionary index rows.
Callers keep CPU source vectors for dictionary values when glyph layout needs them.
Not a generic vertex attribute; consume through dictionary-aware text expansion.Arrow text conversion helpers and dictionary text models.

Other Columns

Arrow data typeGPU
upload
nullableUpload notesShader-facing
use
Higher-level
model support
BinaryVariable-width byte rows need both offsets and value bytes. Support would mirror UTF-8 with explicit offset and byte-buffer bindings.Compute shaders could consume offsets and bytes; not a generic vertex attribute.Adapter-specific payloads such as WKB geometry, compressed attributes, or binary IDs.
FixedSizeBinary🟡Partially supported for interleaved data columns where each row is a fixed-width byte record.Not a generic scalar/vector attribute; consume through an explicit interleaved buffer layout or storage decoding path.Interleaved GPU buffers and packed application-defined row records.
Struct🟡Supported indirectly when the application selects supported child columns by field path.Child fields can become separate shader columns after selection or flattening.Nested style/object columns; GeoArrow separated coordinates can adapt into FixedSizeList values.
MapMap is variable-length key/value Struct data. A future adapter could support maps by reading a specific key from each row.Not a generic shader column; use application-specific key selection or compute lookup after an adapter lowers the map shape.Per-row properties or sparse attributes after application-specific lowering.
DecimalDecimal stores scaled fixed-width integers with precision/scale semantics. Support likely requires conversion to Float32/Float64 or packed integer words plus scale metadata.No generic decimal arithmetic path in shaders.Financial or exact values should be lowered by the application before upload.
LargeUtf8Uses 64-bit offsets; current text paths assume regular Utf8 offsets. Support could downcast offsets when value buffers fit Uint32, or add 64-bit offset helpers.Text shaders could consume after offset normalization; not a generic vertex attribute.Large text columns once normalized to Utf8-style offset buffers.
Dictionary non-Utf8Uploading only dictionary indices loses value semantics. Support needs an adapter that uploads indices plus dictionary value buffers, or lowers values to numeric/text columns.Shader use depends on the decoded value type or explicit lookup tables.Categorical values, palettes, and enums after dictionary-specific lowering.

The Time Columns example prepares aligned DateDay, TimeMillisecond, TimestampMillisecond, and DurationMillisecond event rows together with prepareArrowTemporalGPUVectors(), then renders the same relative Float32 columns as instanced attributes or WebGPU storage rows. The Blinking Stars example uses aligned TimestampMillisecond and DurationMillisecond rows as per-instance visibility windows and pulse periods through the same attribute/storage modes. The path example remains the separate variable-length List<FixedSizeList<Float32, 4>>, DenseUnion LineString, and List<Timestamp> Trips-style stream example.

Shader and Buffer Layout Preliminaries

luma.gl provides separate descriptions of shader attributes and the buffers that provide data for those attributes. The key observation here is that shaders only work with four vertex attribute scalar types (f32, f16, i32, and u32), and there is some flexibility in what binary buffer layouts can feed these declarations.

  • ShaderLayout describes what the shader can accept, such as vec4<f32>.
  • BufferLayout describes how the current table column is stored in memory, such as float32x4, float16x4, or unorm8x4.

This means the shader does not need to be written specifically for every memory representation. A shader attribute is declared using the type used in the shader source code:

const shaderLayout = {
attributes: [{name: 'colors', location: 0, type: 'vec4<f32>'}],
bindings: []
};

Then different Arrow table schemas can use different buffer layouts. For the vec4<f32> described in the shader layout, the following buffer formats are all accepted by the GPU.

Arrow column typeBuffer layout formatNotes
FixedSizeList<Float32, 4>float32x4
FixedSizeList<Float16, 4>float16x4Shader sees f32; compact scene-linear/HDR color
FixedSizeList<Int16, 4>snorm16x4Shader sees f32, normalized to [-1.0, 1.0]
FixedSizeList<Uint16, 4>unorm16x4
FixedSizeList<Int8, 4>snorm8x4
FixedSizeList<Uint8, 4>unorm8x4

Semantic RGB color columns use three source components, but most luma.gl color shader interfaces consume RGBA. Current semantic color adapters treat RGB source forms as unsupported until expansion is implemented. The intended expansion is FixedSizeList<Uint8, 3> to RGBA with alpha 255, and FixedSizeList<Float16 | Float32, 3> to RGBA with alpha 1.0, before buffer layout generation. Alpha values above 1.0 have no portable meaning unless a layer explicitly defines application-specific semantics.

Creating a BufferLayout

Use getArrowBufferLayout() with an Arrow table when Arrow column names match shader attribute names:

import {getArrowBufferLayout} from '@luma.gl/arrow';

const bufferLayout = getArrowBufferLayout(shaderLayout, {
arrowTable: table,
arrowPaths: {
instanceColors: 'properties.color'
}
});

const model = new Model(device, {
vs,
fs,
shaderLayout,
bufferLayout,
vertexCount
});

You can also provide Arrow vectors directly. In this mode, object keys are shader attribute names:

const bufferLayout = getArrowBufferLayout(shaderLayout, {
arrowVectors: {
instanceColors: table.getChild('properties').getChild('color')
}
});

The generated layouts use shader attribute names as buffer names:

[
{name: 'instanceColors', format: 'unorm8x4'}
]

Choosing a Columnar GPU Shape

The main decision is how the shader should read each selected Arrow column.

GPU-facing shapeUse whenTypical Arrow input
Vertex attributeRender or WebGL transform shaders should use fixed scalar/vector inputsScalar numeric columns or 2-, 3-, and 4-component fixed-size lists
Storage bindingWebGPU render or compute shaders should read/write column arrays directlyNumeric vectors that map cleanly to WGSL storage values
Matrix recordOne logical matrix should stay one storage binding on WebGPU but split into vector attributes where neededArrow matrix vectors created by makeArrowMatrix*Vector()
Expanded vectorA compact lookup table must become one row per vertex/instance for attribute pathsexpandArrowVector() plus an integer row map

Use Buffer Schemas and Columnar Records when one logical row needs several attribute views or when you want record-oriented naming that also makes sense next to WGSL storage structs.

The GPU representation does not always stay byte-for-byte identical to the source Arrow representation:

  • attribute paths may expand one logical row into several vertex or instance records so draw-time buffers match shader stepping;
  • storage paths may need type or layout conversion before data becomes WGSL-compatible storage;
  • device buffer-size limits can require preserved or synthetic batching instead of one monolithic GPU upload.

Matrix Columns

@luma.gl/arrow provides explicit matrix vector helpers for the WGSL floating point matrix shapes used by GPU table workflows:

HelperLogical shape
makeArrowMatrix2x2Vector()mat2x2<f32>
makeArrowMatrix2x3Vector()mat2x3<f32>
makeArrowMatrix3x2Vector()mat3x2<f32>
makeArrowMatrix3x3Vector()mat3x3<f32>
makeArrowMatrix4x3Vector()mat4x3<f32>
makeArrowMatrix3x4Vector()mat3x4<f32>
makeArrowMatrix4x4Vector()mat4x4<f32>

The generic makeArrowMatrixVector(shape, values, options) form is available when the shape is selected dynamically.

import {makeArrowMatrix4x4Vector} from '@luma.gl/arrow';

const instanceModelMatrix = makeArrowMatrix4x4Vector(matrixValues, {
order: 'column-major',
layout: 'wgsl-storage'
});

Matrix options:

OptionValuesDefaultMeaning
order'column-major' | 'row-major''column-major'Order of the supplied logical matrix values. GPU-facing vectors are normalized to column-major order.
layout'wgsl-storage' | 'packed''wgsl-storage'Physical row layout. Three-row matrix columns are padded to four floats for WGSL-compatible storage layout.

getArrowMatrixVectorInfo(vector) recovers the stored matrix metadata, including shape, row/column counts, matrix order, physical component count, column stride, value type, and byte stride. prepareArrowMatrixGPUVector() accepts metadata-tagged FixedSizeList<Float32> and FixedSizeList<Float64> matrix columns, then emits canonical Float32 WGSL-storage rows for attribute or storage-buffer use.

deck.gl SimpleMeshLayer and ScenegraphLayer expose getTransformMatrix as a 4x4 accessor, but their attribute updater drops the final row and uploads three vec3 model-matrix columns plus one vec3 translation. Arrow callers targeting those layers can store that GPU-facing affine payload directly as mat4x3, or accept a 4x4 source and explicitly truncate to the affine mat4x3 payload before binding it.

Public matrix types:

TypeMeaning
ArrowMatrixShapeSupported shape identifier such as 'mat4x4'.
ArrowMatrixOrderInput value order: 'column-major' or 'row-major'.
ArrowMatrixLayoutPhysical vector layout: 'wgsl-storage' or 'packed'.
ArrowMatrixVectorOptionsOptions accepted by the matrix builders.
ArrowMatrixVectorInfoMetadata recovered by getArrowMatrixVectorInfo().
ArrowFloat32Matrix2x2 through ArrowFloat32Matrix4x4Fixed-size Arrow row types for each supported matrix shape.

One Matrix Column, Two Consumption Paths

On WebGPU, one matrix Arrow column can remain one storage binding:

const shaderLayout = {
attributes: [],
bindings: [
{
name: 'instanceModelMatrix',
type: 'read-only-storage',
group: 0,
location: 0
}
]
};
@group(0) @binding(auto)
var<storage, read> instanceModelMatrix: array<mat4x4<f32>>;

On attribute-oriented paths, map each vector attribute back to the same matrix column:

const shaderLayout = {
attributes: [
{name: 'instanceModelMatrixCol0', location: 0, type: 'vec4<f32>'},
{name: 'instanceModelMatrixCol1', location: 1, type: 'vec4<f32>'},
{name: 'instanceModelMatrixCol2', location: 2, type: 'vec4<f32>'},
{name: 'instanceModelMatrixCol3', location: 3, type: 'vec4<f32>'}
],
bindings: []
};

const bufferLayout = getArrowBufferLayout(shaderLayout, {
arrowTable,
arrowPaths: {
instanceModelMatrixCol0: 'instanceModelMatrix',
instanceModelMatrixCol1: 'instanceModelMatrix',
instanceModelMatrixCol2: 'instanceModelMatrix',
instanceModelMatrixCol3: 'instanceModelMatrix'
}
});

The Arrow helper recognizes the matrix metadata and lowers the matrix row through the engine BufferSchema path into one shared interleaved buffer layout.

Temporal Columns

prepareArrowTemporalGPUVector() and prepareArrowTemporalGPUVectors() normalize supported Arrow temporal logical columns into relative Float32 GPU vectors while retaining the original Arrow unit:

const preparedTemporalColumns = await prepareArrowTemporalGPUVectors(device, {
eventStarts,
eventDurations
});

const eventTable = new GPUTable({
vectors: {
eventStarts: preparedTemporalColumns.eventStarts.temporal,
eventDurations: preparedTemporalColumns.eventDurations.temporal
}
});

Absolute Date, Time, and Timestamp columns choose the first valid scalar or flattened list value as their relative origin when prepared metadata does not already carry one. Duration columns use origin 0. The chosen kind, unit, origin, origin policy, and timestamp timezone metadata are persisted on the prepared field so repeated transforms and append flows can reuse the same comparison domain.

Prepared scalar temporal rows can be instanced attributes or WebGPU storage rows. Prepared List<temporal> rows preserve list offsets and are intended for storage/compute or model-specific expansion, such as ArrowStorageTripsPathModel. Interval and List<Interval> are intentionally deferred because Arrow interval leaves have compound calendar semantics rather than one scalar source unit that can be origin-subtracted without an explicit application contract.

DGGS Cell Keys

DGGS helpers provide a WebGPU-only path from compact global grid IDs to Uint64 key storage and rendered cell boundaries:

const preparedKeys = prepareDggsCellKeyGPUVector(device, cellIds, {
encoding: 'geohash'
});
const preparedPaths = prepareDggsCellPathGPUVector(device, preparedKeys.keys, {
encoding: 'geohash'
});

prepareDggsCellKeyGPUVector() accepts UTF-8 geohash, quadkey, S2, A5, or H3 IDs and parses them on the GPU into Uint64 rows. prepareDggsCellPathGPUVector() accepts those Uint64 rows and emits closed List<FixedSizeList<Float32, 2>> boundary paths by default. Pass coordinateFormat: 'fp64-split' to emit List<FixedSizeList<Float32, 4>> paths with components ordered as [longitudeHigh, latitudeHigh, longitudeLow, latitudeLow]. This is a compatibility layout only: DGGS boundary math currently remains Float32, so low components are zero until true higher-precision decode math is added. The dggs shader module exposes the matching WGSL Uint64 word order and cell-boundary helpers when a shader should read keys directly. See the Global Grids example.

Storage-Selected Table Columns

GPURecordBatch and GPUTable also select Arrow columns referenced by shader bindings of type 'storage' or 'read-only-storage'. Selected storage columns:

  • appear in gpuVectors by binding name;
  • appear in bindings by binding name;
  • contribute fields to the GPU-facing Arrow schema;
  • are rebound batch-by-batch by GPUTableModel.drawBatches(renderPass).

This keeps table-backed WebGPU render paths compact. A column such as instanceModelMatrix can be uploaded once through the table machinery and bound directly to a WGSL storage array without four separate matrix-column bindings.

Storage-selected columns and attribute-selected columns must use distinct shader input names. A single name cannot be both an attribute and a storage binding in one GPURecordBatch.

Expanding Compact Column Values

expandArrowVector(vector, rowMapping, nullValue?) gathers rows from one numeric Arrow vector into a new contiguous Arrow vector. It accepts either an integer typed array or an integer Arrow vector as the row mapping.

Use it when a compact table should become vertex- or instance-aligned data for an attribute path. The Matrices: FixedSizeList<Float32, 16> example uses this idea for WebGL face colors: a six-row face-color vector is expanded by cube faceIndex rows into a vertex-aligned color vector.

Supported source vectors:

  • scalar numeric Arrow vectors;
  • FixedSizeList numeric vectors.

The helper rejects unsupported vector types, non-integer row mappings, negative indices, and out-of-range indices.

ArrowVectorRowMapping is the exported type name for the accepted mapping input. ArrowVectorNullValue is the exported type name for the optional null-row fallback. Scalar numeric vectors accept a numeric nullValue. FixedSizeList numeric vectors accept an array-like nullValue with exactly the list size number of components. If nullValue is omitted, null source rows keep the existing values-buffer copy behavior.

For FixedSizeList vectors, the parent row may be nullable and will expand to the supplied constant fallback. Nullable child values inside valid rows are not supported by this helper.

Closing Nested Arrow Paths

closeArrowPaths() turns logical closed-path flags into explicit closing vertices without mutating the input vector. The helper accepts GPU-resident List<FixedSizeList<Float32>> path rows with one to four components per vertex, an Arrow Bool vector with one closed flag per row, and a non-negative epsilon. Those Float32 rows can be absolute path coordinates or origin-relative delta coordinates produced from Float64 path preparation.

import {closeArrowPaths} from '@luma.gl/arrow';

const normalizedPaths = await closeArrowPaths(device, {
paths,
closed,
epsilon: 1e-5
});

The output preserves Arrow chunk boundaries. Open rows, rows already closed within epsilon, empty rows, and single-point rows are unchanged. Closed rows whose first and last vertices differ receive one appended copy of the first vertex. For Float64 source paths, closure happens after CPU conversion to delta space; if the original last point equals the first, both deltas are zero, and injected closing vertices append the first delta, usually [0, 0, ...].

Preparing Arrow Paths for Rendering

Use ArrowPathModel.prepareGPUVectors() when raw Arrow vectors need to become attribute-renderer inputs. Float32 paths upload unchanged unless closed flags are supplied. Float64 paths are converted on the CPU into stable per-row Float32 deltas from the first point of each path. The prepared object also owns CPU Float64 origins and updates view-space origin buffers when the view or model matrix changes.

const prepared = await ArrowPathModel.prepareGPUVectors(device, {
paths,
colors,
widths,
closed
}, {
closeEpsilon: 1e-5
});

const pathModel = new ArrowPathModel(device, prepared.pathProps);
const storagePathModel = new ArrowStoragePathModel(device, prepared.storagePathProps);

prepared.updateViewOrigins({modelViewMatrix});

pathModel.destroy();
storagePathModel.destroy();
prepared.destroy();

For storage-only WebGPU paths, use the storage preparation entrypoint instead:

const preparedStorage = await ArrowStoragePathModel.prepareGPUVectors(device, {
paths,
colors,
widths,
closed
}, {
closeEpsilon: 1e-5
});

const storagePathModel = new ArrowStoragePathModel(device, preparedStorage.storagePathProps);

That path uploads raw Float64 list payloads temporarily, converts them once into Float32 per-row deltas with sub_fp64u32_to_f32, then discards the transient Float64 GPU upload before returning the prepared storage props.

Path constructors stay GPUVector/prepared-state only; raw Arrow path vectors belong at the preparation boundary. For Float64 paths, shaders should transform deltas with a zero homogeneous component and add the CPU-updated view origin before projection.

ArrowStoragePathModel keeps the default storage path record compact: each generated segment stores three u32 words (segmentStartPointIndex, segmentFlags, and globalRowIndex) plus a persistent vec4<u32> path range per source row. The storage shader receives pathRanges automatically and can derive end, previous, and next point indices from those ranges and flags. Custom storage shader layouts that request segmentEndPointIndices, segmentPreviousPointIndices, or segmentNextPointIndices keep the legacy six-word segment record layout.

GPUTableModel is the generic tables-layer wrapper that combines GPUTable with Model. It draws preserved GPU batches, syncs table row counts into the selected draw count, and leaves table ownership with the caller. Convert Arrow tables explicitly with makeGPUTableFromArrowTable() before constructing the model.

Use model.drawBatches(renderPass) to draw preserved static GPU batches with one pipeline layout while rebinding only batch-local attribute buffers and storage bindings. Packed tables naturally reduce that to fewer draw calls.

Mesh Arrow Geometry

GPUTableGeometry exposes one packed static GPUTable as GPUGeometry. ArrowTableGeometry and makeGPUGeometryFromArrow() convert loaders.gl- compatible Mesh Arrow tables into that generic geometry surface. These helpers support mesh and point-cloud tables that use glTF-style column names such as POSITION, NORMAL, COLOR_0, and TEXCOORD_0.

import {
ArrowTableGeometry,
makeGPUGeometryFromArrow,
type ArrowMeshTable
} from '@luma.gl/arrow';
import {Model} from '@luma.gl/engine';

const geometry = new ArrowTableGeometry(device, {
arrowMesh,
interleaved: true
});

const equivalentGeometry = makeGPUGeometryFromArrow(device, {
arrowMesh,
interleaved: true
});

const model = new Model(device, {
vs,
fs,
shaderLayout,
geometry
});

The local ArrowMeshTable type is structural and does not add a dependency on loaders.gl. It intentionally mirrors loaders.gl MeshArrowTable: a wrapper with shape: 'arrow-table', topology, optional top-level indices, and raw data: arrow.Table.

Mesh Arrow tables use one row per vertex. Vertex attributes are scalar numeric or FixedSizeList<numeric, 1 | 2 | 3 | 4> columns. ArrowTableGeometry normalizes common glTF semantics to luma.gl shader attribute names:

Mesh Arrow columnShader attribute
POSITIONpositions
NORMALnormals
COLOR_0colors
TEXCOORD_0texCoords
TEXCOORD_1texCoords1

Unknown column names are preserved unless arrowPaths maps a shader attribute name to a specific Arrow column name.

Indexed Mesh Arrow tables follow the loaders.gl convention: a lowercase indices: List<Int32> column stores the full primitive index list in row 0, and remaining vertex rows are null. ArrowTableGeometry uploads those indices as a separate GPU index buffer. If the wrapper has a top-level indices accessor and the Arrow table has no indices column, that accessor is used as a fallback.

By default, ArrowTableGeometry packs all selected vertex attributes into one interleaved vertex buffer and keeps indices separate. Pass interleaved: false to upload one vertex buffer per attribute.

Streaming sources stay application-owned. Convert each yielded Arrow record batch once, keep the resulting immutable GPURecordBatch, and add it to the retained GPUTable:

import {makeGPURecordBatchFromArrowRecordBatch, makeGPUTableFromArrowTable} from '@luma.gl/arrow';

const gpuTable = makeGPUTableFromArrowTable(device, new arrow.Table([firstRecordBatch]), {
shaderLayout
});

for await (const recordBatch of remainingRecordBatches) {
gpuTable.addBatch(makeGPURecordBatchFromArrowRecordBatch(device, recordBatch, {shaderLayout}));
model.setInstanceCount(gpuTable.numRows);
}

Text and other generated-geometry paths can then expand each preserved source GPU batch independently, and may fan one source GPURecordBatch out into multiple generated render batches when device buffer limits require splitting.

Supported Shader Types

Arrow scalar numeric columns map to scalar shader attributes. Arrow FixedSizeList<numeric, 2 | 3 | 4> columns map to vector shader attributes.

Shader attribute typePortable Arrow columns
f32Float32, Float16, Int8, Uint8, Int16, Uint16
vec2<f32>FixedSizeList<Float32, 2>, FixedSizeList<Float16, 2>, FixedSizeList<Int8, 2>, FixedSizeList<Uint8, 2>, FixedSizeList<Int16, 2>, FixedSizeList<Uint16, 2>
vec3<f32>FixedSizeList<Float32, 3>
vec4<f32>FixedSizeList<Float32, 4>, FixedSizeList<Float16, 4>, FixedSizeList<Int8, 4>, FixedSizeList<Uint8, 4>, FixedSizeList<Int16, 4>, FixedSizeList<Uint16, 4>
f16Float16, Int8, Uint8, Int16, Uint16
vec2<f16>FixedSizeList<Float16, 2>, FixedSizeList<Int8, 2>, FixedSizeList<Uint8, 2>, FixedSizeList<Int16, 2>, FixedSizeList<Uint16, 2>
vec3<f16>None in portable WebGPU layouts
vec4<f16>FixedSizeList<Float16, 4>, FixedSizeList<Int8, 4>, FixedSizeList<Uint8, 4>, FixedSizeList<Int16, 4>, FixedSizeList<Uint16, 4>
i32Int8, Int16, Int32
vec2<i32>FixedSizeList<Int8, 2>, FixedSizeList<Int16, 2>, FixedSizeList<Int32, 2>
vec3<i32>FixedSizeList<Int32, 3>
vec4<i32>FixedSizeList<Int8, 4>, FixedSizeList<Int16, 4>, FixedSizeList<Int32, 4>
u32Uint8, Uint16, Uint32
vec2<u32>FixedSizeList<Uint8, 2>, FixedSizeList<Uint16, 2>, FixedSizeList<Uint32, 2>
vec3<u32>FixedSizeList<Uint32, 3>
vec4<u32>FixedSizeList<Uint8, 4>, FixedSizeList<Uint16, 4>, FixedSizeList<Uint32, 4>

Component counts must match. For example, FixedSizeList<Uint8, 4> can feed vec4<f32>, but not vec3<f32>.

For f32 and f16 shader attributes, integer Arrow columns are read through normalized vertex formats (snorm* for signed integers and unorm* for unsigned integers).

WebGPU Portability

WebGPU does not support every vertex format that WebGL can read. In particular, 3-component 8-bit and 16-bit integer-backed columns are not portable. By default, getArrowBufferLayout() rejects those mappings with an error.

Shaders that declare f16 attributes have an additional capability requirement. Before creating a WebGPU device, check adapter.features.has('shader-f16'), request that feature when creating the device, and include enable f16; in WGSL. Without that feature, WebGPU rejects shader modules that use f16 types.

For WebGL-only use cases, opt in to WebGL-only formats:

const bufferLayout = getArrowBufferLayout(shaderLayout, {
arrowTable: table,
allowWebGLOnlyFormats: true
});

For portable WebGPU layouts, prefer Float32 for vec3<f32> attributes or pad 8-bit and 16-bit vector data to four components.