Three.js Network Graph — 3D Nearest-Neighbor Node Visualization

Three.js Network Graph · Animations · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Uniform spherical distribution: nodes scattered with proper spherical-coordinate sampling, not a clustered cube
Nearest-neighbor edge generation: every node connects to its three closest neighbors, deduplicated per pair
Single LineSegments draw call: every edge shares one BufferGeometry, redrawn in place every frame
Original-position wobble: each node bobs around its own saved base position, never drifting or accumulating
OrbitControls with auto-rotate: the camera drifts on its own but responds instantly to manual dragging
Emissive node material: glowing spheres read clearly against a dark background from any angle
Fully self-contained: no external graph library, layout algorithm import, or dataset required
Loaded entirely from a CDN: no npm install, bundler, or build step

About this UI Snippet

How to Build a 3D Network Graph in Three.js With Nearest-Neighbor Edges

Screenshot of the Three.js Network Graph snippet rendered live

The Three.js Network Graph snippet scatters 48 glowing nodes through a spherical volume, connects each one to its nearest neighbors, and animates every node bobbing gently in place while the connecting lines redraw every frame to follow them — a floating, organic node-and-edge visualization built with core Three.js and its OrbitControls addon, both loaded from a CDN.

Uniform random points inside a sphere, not a cube or a flat plane

Scattering points with plain Math.random() on each of X, Y, and Z independently produces a cube-shaped distribution with visibly denser corners — not a sphere. This snippet instead samples each node's position using spherical coordinates (theta, phi) plus a cube-root-scaled random radius, which is the standard formula for a *uniform* random distribution inside a sphere's volume rather than clustered toward its center or corners. The result is a genuinely spherical, evenly-distributed node cloud from any viewing angle — essential for a graph that's meant to be dragged and viewed from all sides.

Nearest-neighbor edges, not fully random or fully connected

Connecting every node to every other node would produce an unreadable tangle of over a thousand lines; connecting nodes to purely random partners would look arbitrary rather than structural. Instead, each node computes its distance to all 47 others, sorts by distance, and connects to its three geometrically closest neighbors — deduplicated so a pair only gets one edge even if both nodes pick each other. This nearest-neighbor rule is what makes the result look like an organic, plausible network topology rather than a random scribble or an overwhelming mesh.

One LineSegments object for every edge, updated every frame

Rather than one THREE.Line per connection, all edges share a single THREE.LineSegments object backed by one flat BufferGeometry — every pair of positions in that buffer represents one edge's two endpoints. Every frame, after nodes move, the loop rewrites each edge's two endpoint positions directly into that shared buffer and flags needsUpdate, keeping the whole connection mesh in one GPU-friendly draw call no matter how many edges exist.

Independent per-node bobbing keeps the shape stable

Each node stores its own base position (its true, structural location) plus a random phase and speed. Every frame, the node's *rendered* position is offset from that saved base by a small sine/cosine wobble — never accumulated onto the previous frame's already-wobbled position. This is the same original-position-plus-offset principle used to keep any procedural animation bounded: the network visibly breathes and drifts, but it never drifts away from its overall spherical shape or collapses over time.

OrbitControls with auto-rotate for a "floating in space" feel

The camera auto-rotates slowly on its own via OrbitControls' autoRotate, while still responding immediately to a manual drag at any time — damping smooths the transition back to auto-rotation once the visitor lets go, so the graph never feels like it's fighting the visitor's input.

Where this pattern applies

The scatter-plus-nearest-neighbor-edges technique generalizes directly to knowledge graphs, dependency visualizations, social network diagrams, or any dataset where "things relate to nearby things." Pair it with a particle network for a 2D-versus-3D comparison, or contrast its organic, floating structure against the rigid, hierarchical orbits of the solar system snippet.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to reverse-engineer the spherical sampling formula or the neighbor-selection logic by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the node positions use spherical coordinates with a cube-root-scaled radius instead of three independent Math.random() calls, or how the nearest-neighbor edge deduplication avoids drawing the same connection twice. The same assistant can help optimize it, for instance checking whether the O(n squared) nearest-neighbor search could be replaced with a spatial partitioning structure for a much larger node count, or whether the wobble calculation could be vectorized. It is also useful for extending the graph: ask it to color edges by connection strength or node degree, animate new nodes and edges appearing over time instead of all at once, or add click-to-highlight so hovering a node emphasizes only its direct connections. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a floating "3D network graph" in plain HTML, CSS, and JavaScript using Three.js and its OrbitControls addon, both loaded from a CDN (no bundler, no build step, no external graph library).

Requirements:
- A full-viewport canvas with a WebGLRenderer sized to match it, updated on window resize including camera aspect ratio, plus OrbitControls with damping and idle auto-rotation enabled so the camera drifts on its own but responds instantly to manual dragging.
- Generate at least 40 node positions uniformly distributed inside a sphere's volume using proper spherical coordinate sampling (a random azimuthal angle, a polar angle derived from the arc-cosine of a linearly-mapped random value, and a radius scaled by the cube root of a random value) — do not sample X, Y, and Z independently with plain random numbers, since that produces a cube-shaped distribution instead of a spherical one.
- Render each node as a small glowing sphere mesh with an emissive material.
- For every node, calculate its distance to every other node, sort by distance, and connect it to its three nearest neighbors, deduplicating so that a mutually-nearest pair of nodes produces only one edge rather than two.
- Render all edges as a single THREE.LineSegments object backed by one shared BufferGeometry (not one Line object per edge), where every pair of positions in the buffer represents one edge's two endpoints.
- Store each node's original ("base") position separately from its currently rendered position. Every animation frame, offset each node's rendered position from its saved base using a small sine and cosine wobble driven by a shared time value plus a random per-node phase and speed — never accumulate the wobble onto the node's position from the previous frame.
- Every frame, after updating node positions, rewrite each edge's two endpoint coordinates into the shared line buffer based on its two connected nodes' current positions, and flag the buffer as needing a GPU update once per frame.

Step by step

How to Use

  1. 1
    Load both CDN scriptsAdd three.min.js and OrbitControls.js from the CDN panel, in that order.
  2. 2
    Paste HTML, CSS, and JS48 nodes and their nearest-neighbor connections appear immediately, gently bobbing.
  3. 3
    Drag to look aroundClick and drag to manually rotate the camera; release and it resumes its slow auto-rotation.
  4. 4
    Adjust node countChange NODE_COUNT for a sparser or denser graph; edge count scales automatically.
  5. 5
    Tune neighbor connectionsChange .slice(0, 3) to connect each node to more or fewer of its nearest neighbors.
  6. 6
    Resize the windowRenderer size and camera aspect ratio update automatically on resize.

Real-world uses

Common Use Cases

Knowledge graph and data visualizations
A natural fit for representing concepts, dependencies, or relationships as an explorable 3D structure.
Tech and infrastructure landing pages
Represents distributed systems, microservices, or network topology as an interactive hero visual.
Teaching nearest-neighbor algorithms
A concrete, visual demonstration of distance sorting and neighbor selection applied to real 3D geometry.
Portfolio and agency showpieces
Demonstrates procedural graph generation and real-time geometry updates, not just a static 3D render.
Loading and menu backgrounds
A slowly drifting, explorable node cloud gives a loading screen ambient motion worth looking at.
Social and community platform visuals
Represent user connections or community structure as a literal, explorable 3D social graph.

Got questions?

Frequently Asked Questions

Each node's position is generated using spherical coordinates — a random angle theta, a random angle phi derived from acos(2v - 1), and a radius scaled by the cube root of a random value. This specific formula is required for a mathematically uniform distribution inside a sphere's volume; sampling X, Y, and Z independently with plain random numbers instead produces a cube-shaped distribution with visibly denser corners.

Every node calculates its distance to all other nodes, sorts them by distance, and connects to its three closest neighbors. Connections are deduplicated with a shared key so a pair of mutually-nearest nodes only produces one edge, not two overlapping ones. This nearest-neighbor rule produces a natural-looking network topology instead of either a fully connected tangle or arbitrary random pairs.

A single THREE.LineSegments object backed by one shared BufferGeometry lets every edge render in one GPU draw call. Every frame, each edge's two endpoint positions are rewritten directly into that shared position buffer based on the current positions of its two connected nodes, then the whole buffer is flagged as needing an update once.

Offsetting from a fixed, saved base position every frame keeps the wobble bounded and the overall graph shape stable indefinitely. Accumulating the wobble onto whatever position the node ended up at the previous frame would cause nodes to drift further and further from their intended location over time.

Yes. Raise NODE_COUNT for more nodes (edge count and distance-sorting cost scale accordingly), and change the .slice(0, 3) call to a larger number to connect each node to more of its nearest neighbors for a denser mesh of edges.

Yes. Click JSX for a React component, Vue for a Vue 3 SFC, Angular for a standalone component, or Tailwind for a React + Tailwind CSS utility-class version. Build the nodes, edges, and controls inside a mount effect so the nearest-neighbor calculation only runs once, and call controls.dispose() plus renderer.dispose() on cleanup to release the WebGL context and event listeners.