@tecnomancy/vort3x
v0.1.1
Published
3D force-directed graph with floating labels — plug data, get insight.
Maintainers
Readme
vort3x
3D force-directed graph with floating labels — plug data, get insight.
vort3x is a TypeScript-native wrapper around 3d-force-graph built for mind maps, OSINT graphs, and relationship visualizations.
Every node renders a floating label in 3D space — no hover required. Data flows in as a typed { nodes, links } object and comes out as an interactive vortex of connections.
Install
npm install @tecnomancy/vort3xPeer dependency:
three >= 0.150.0
Quick Start
import { createVort3x } from '@tecnomancy/vort3x'
const graph = createVort3x('#graph', {
nodes: [
{ id: 'a', label: 'Node A', type: 'entity', subject: 'group1', risk: 'high' },
{ id: 'b', label: 'Node B', type: 'email', subject: 'group1', risk: 'mid' },
{ id: 'c', label: 'Node C', type: 'ip', subject: 'group2', risk: 'low' },
],
links: [
{ source: 'a', target: 'b' },
{ source: 'a', target: 'c', color: '#ff444455' },
],
})API
createVort3x(element, data, options?)
| Param | Type | Description |
|---|---|---|
| element | string \| HTMLElement | CSS selector or DOM element |
| data | Vort3xData | { nodes, links } |
| options | Vort3xOptions | Theme, callbacks, particles |
Returns a Vort3xInstance.
Vort3xNode
interface Vort3xNode {
id: string // unique identifier
label: string // floating label text
type: string // drives node color (see theme)
layer?: 'net' | 'identity' | 'code' | 'shared'
subject?: string // group/owner — used for filtering
size?: number // sphere radius multiplier (default 6)
risk?: 'high' | 'mid' | 'low' | 'info' // sub-label color
desc?: string // description for click handlers
}Vort3xLink
interface Vort3xLink {
source: string // node id
target: string // node id
color?: string // hex with alpha e.g. '#ff444455'
}Vort3xOptions
interface Vort3xOptions {
theme?: Partial<Vort3xTheme>
onNodeClick?: (node: Vort3xNode) => void
particles?: boolean // link directional particles (default true)
}Instance methods
// Filter by predicate — hides non-matching nodes and their links
graph.filter(node => node.subject === 'group1')
// Reset to full graph
graph.resetFilter()
// Fly camera to a node
graph.focusNode('a')
// Remove the graph from the DOM
graph.destroy()Theme
The default theme maps node.type to colors:
import { defaultTheme } from '@tecnomancy/vort3x'
const graph = createVort3x('#graph', data, {
theme: {
...defaultTheme,
nodeColors: {
...defaultTheme.nodeColors,
entity: '#aa44ff',
custom: '#00ccff',
},
},
})Default node types and colors:
| Type | Color |
|---|---|
| layer | #00ff88 |
| ip | #ff4444 |
| dns | #ff7744 |
| asn | #ff6655 |
| geo | #44ffaa |
| infra | #ff8800 |
| vuln | #ff3333 |
| github | #4488ff |
| email | #66aaff |
| entity | #aa44ff |
| secret | #ffaa00 |
| deploy | #ffcc44 |
Patterns
OSINT graph with multi-layer filtering
import { createVort3x } from '@tecnomancy/vort3x'
const graph = createVort3x('#graph', data, {
onNodeClick: node => {
document.getElementById('detail-title').textContent = node.label
document.getElementById('detail-desc').textContent = node.desc ?? ''
},
})
// Filter by layer
document.getElementById('btn-net').addEventListener('click', () => {
graph.filter(n => n.layer === 'net' || n.type === 'layer')
})
// Filter by risk
document.getElementById('btn-risk').addEventListener('click', () => {
graph.filter(n => n.risk === 'high' || n.risk === 'mid')
})
// Reset
document.getElementById('btn-all').addEventListener('click', () => {
graph.resetFilter()
})Mind map
const graph = createVort3x('#mindmap', {
nodes: [
{ id: 'root', label: 'Project', type: 'layer', size: 14 },
{ id: 'fe', label: 'Frontend', type: 'entity', subject: 'tech' },
{ id: 'be', label: 'Backend', type: 'entity', subject: 'tech' },
{ id: 'react', label: 'React', type: 'deploy', subject: 'tech' },
{ id: 'nest', label: 'NestJS', type: 'deploy', subject: 'tech' },
],
links: [
{ source: 'root', target: 'fe' },
{ source: 'root', target: 'be' },
{ source: 'fe', target: 'react' },
{ source: 'be', target: 'nest' },
],
}, { particles: false })Design Principles
- Labels first. Every node shows its label floating in 3D space — no hover, no click required to read the graph.
- Typed data in, typed instance out. No config objects with unknown shapes.
- alchemy inside. Internal data transformations use
@tecnomancy/alchemy—pipeandfilterfor link resolution and node filtering. - Thin wrapper. vort3x does not re-implement physics. It configures and extends 3d-force-graph with sane defaults for information graphs.
- Theme-driven colors. Node type drives color — consistent visual language across any dataset.
Requirements
- TypeScript ≥ 5.0
three≥ 0.150.0 (peer dependency)"moduleResolution": "bundler"or"node16"
License
MIT © tecnomancy
