Skip to main content

Overview

A social network, service-dependency map, transaction investigation, or citation graph becomes easier to understand when its relationships are visible. This WebGPU-only example starts with 1,024 actual vertices and offers fourteen real graph populations from 128 to 1,048,576 vertices. Its largest network retains 1,048,576 GPU-resident vertices and 2,097,343 original directed edges while real deck.gl layers render graph analytics directly from GPU memory.

Every source vertex remains rendered. Only visible original edge detail is bounded to 65,536 edges; the complete graph, original edge batches, and adjacency remain resident.

Preparing GPU experienceluGraph + deck.gl Network ExplorerLoading GPU graph analytics, progressive layout, and direct deck.gl layers.

Why combine luGraph and deck.gl?

An application that already owns GPU graph data should not need to download every edge, construct a JavaScript object for every vertex, and upload changing positions again just to render a network. luGraph computes relationship data; deck.gl already understands application views, layer lifecycle, camera controls, picking, and GPU drawing. This integration lets each system keep its existing responsibility while sharing the same actual GPU allocations.

A reusable LuGraphDeckEffect schedules luGraph work inside the existing deck.gl frame. LuGraphNodeLayer and LuGraphEdgeLayer consume its results directly, and an OrthographicView provides familiar pan and zoom. Those reusable adapters and all graph integration-specific deck.gl imports live in the existing private @deck.gl-community/arrow-layers module. Graph analytics remain in @luma.gl/experimental/lugraph, while this unpackaged example imports the adapter's public symbols without importing @deck.gl/core directly.

When should I use this integration?

Use this approach when a deck.gl application already has GPU-resident relationship data or will reuse graph analytics across many interactive frames:

  • Social and communication networks: reveal influential accounts, disconnected groups, and genuine local communities, plus introductions within a selected number of hops.
  • Service and package dependencies: inspect a failed service's reachable dependencies and identify tightly connected ownership groups or structurally important systems.
  • Transaction and fraud investigations: follow relationships around an account and expose connected groups of counterparties or coordinated communities within one larger network.
  • Knowledge and citation maps: compare incoming PageRank influence with visual neighborhoods and relationship structure.

A small one-off graph that starts and stays in JavaScript may be simpler to process on the CPU. Initial GPU upload, pipeline compilation, queue submission, and requested picking still have real costs; keeping a graph resident does not automatically make every workload faster.

How the GPU-resident frame works

  1. Upload the demonstration fixture once for the selected graph size. Its source and target relationship columns retain their three original aligned batches: one nonempty batch, one intentionally empty batch, and a second nonempty batch. Changing the slider creates an actual new graph; its displayed population is not a multiplied sample.
  2. Analyze the graph in the first frame. LuGraphDeckEffect builds forward and reverse compressed adjacency, computes exact vertex degree and normalized dangling-aware PageRank, and produces both weak-component and deterministic label-propagation community labels. Weak components describe connectivity; community labels can separate tightly connected groups inside one component. Their bounded iteration budget does not guarantee convergence.
  3. Update interaction and layout in later frames. GPU breadth-first search highlights the selected vertex's bounded neighborhood when selection changes. Exact, flat-grid spatial, or sampled force layout progressively advances the existing node positions.
  4. Draw the same allocations through real deck.gl layers. LuGraphNodeLayer binds the actual float32x2 layout allocation as an instanced vertex attribute. Actual community, component, degree, PageRank, and distance results control color; PageRank, degree, or a uniform value controls size. One LuGraphEdgeLayer draws each nonempty original source/target batch directly; the empty middle batch remains in the complete graph and is never concatenated or repacked. Visible edge instances can be capped without dropping any resident edge or vertex.

The effect records compute into deck.gl's own WebGPU command encoder; deck.gl owns queue submission. The node-position allocation has both Buffer.STORAGE and Buffer.VERTEX usage, so simulation can update the exact same memory that the node layer fetches as an instance attribute.

Try the controls

  • Hover a node to inspect its stable original vertex identifier and whether it is pinned.
  • Click a node to select it and highlight its GPU-computed neighborhood; click empty space to clear the selection.
  • Change the graph size to rebuild real resident vertices across fourteen keyboard-accessible steps, from 128 to 1,048,576.
  • Choose a layout mode to compare automatic, exact, spatial, and sampled GPU force paths.
  • Choose a node color mode to inspect community, weak-component, degree, PageRank, or neighborhood-distance results.
  • Choose a node size mode to compare PageRank, degree, or uniform-size vertices.
  • Toggle original edges to show or hide their bounded visible instances without changing the complete resident source batches.
  • Pause or resume the layout while preserving the live graph and interaction controls.
  • Adjust neighborhood depth to include between zero and eight unweighted relationship hops.
  • Drag a node to move its existing GPU coordinates and pin it while the remaining graph moves.
  • Release pins to let every pinned vertex move again.
  • Reset layout to restore deterministic initial positions for unpinned vertices.
  • Pan and zoom by dragging empty space or using the scroll wheel in the orthographic view.

These controls update small existing GPU selection, depth, pin, or position buffers; they do not rebuild a JavaScript graph or download vertex columns for every animation frame.

What actually stays on the GPU

After each selected fixture is uploaded, all original source and target edge chunks, compressed adjacency, degree counts, PageRank scores, weak-component identifiers, label-propagation communities, breadth-first distances, neighborhood masks, and progressive layout positions stay resident. Rendering does not concatenate edge batches, copy positions into a second vertex allocation, or read graph columns back to the CPU. At the largest setting, every one of the 1,048,576 original vertices remains rendered; the 2,097,343 original directed edges remain resident even when only 65,536 original edges are visible.

The sampled-layout helper is supplied by the example as an injected callback. The existing private @deck.gl-community/arrow-layers package owns Deck imports, effects, and layers without importing the example; the public luGraph package remains renderer-independent.

Interaction is not magically transfer-free: JavaScript writes explicit selection controls, pin flags, or dragged coordinates when requested. deck.gl's native asynchronous WebGPU picking also returns a requested selected-vertex result to JavaScript. The returned PickingInfo.index is the stable original vertex identifier; it is not a download of graph positions, PageRank scores, or edge columns. deck.gl owns that picking implementation and its transfer size, so this example does not claim the separate native luGraph explorer's custom integer-readback contract.

Boundaries and performance

Automatic layout changes its actual GPU workload as the graph grows:

  • Through 512 vertices: exact force-directed layout evaluates all vertex pairs and costs O(V² + E) per iteration.
  • From 1,024 through 8,192 vertices: the explicit flat-grid spatial approximation evaluates nearby forces exactly and combines sufficiently distant cells.
  • From 16,384 vertices: sampled layout evaluates every real incident edge plus four deterministic repulsion samples for each original vertex, costing O(E + 4V) per iteration.
  • From 65,536 vertices: each original vertex is rendered once as a compact GPU point; only visible original edge instances are capped at 65,536.

The four-sample path is intentionally approximate; it is not an exact million-body simulation, Barnes–Hut, or ForceAtlas2. Degree and adjacency cover the complete resident graph; PageRank, weak components, and label-propagation communities use explicitly bounded iterations and cannot claim convergence without a genuine convergence result.

The live inspector distinguishes total resident vertices and edges from displayed edge detail, reports frame cadence, actual adapter and memory data, and labels CPU encoding separately from GPU execution. It does not invent convergence, timestamps, GPU execution durations, or synchronization. WebGPU is required; no CPU rendering or analytics fallback is implied.

See the luGraph API guide for graph ownership, overflow handling, the separate optional spatial-layout approximation, and opt-in live CPU/GPU benchmarks. The native luGraph explorer demonstrates a different renderer, a richer graph-inspector dashboard, and its own explicitly documented picking path.