Development of Network Graph Visualization on a Website

Development of Network Graph Visualization on a Website

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1287
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    986
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1038
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1112
  • image_website-_0.webp
    Website development for Red Pear
    556

Development of Network Graph Visualization on a Website

Note: when the number of dependencies in a microservice architecture exceeds a hundred, standard lists stop working. Visualization of relationship graphs (Network Graph) is the only way to see which services depend on an outdated database or where cycles form. In practice, a microservice graph often contains 10,000+ nodes — without force-directed layout and WebGL, the render would hang the browser. The problem worsens when data is dynamically updated: each API request rebuilds the graph, and without optimization, interactivity becomes impossible. On one client project, we reduced graph load time from 12 to 2 seconds through node virtualization and LOD rendering, saving the client 200,000 rubles per month in error detection. Without such approaches, the graph remains unreadable and useless for analysis.

Choosing the right layout algorithm is a critical decision that determines UX. Force-directed layout provides natural clustering, allowing quick identification of isolated nodes and dense groups. As a result, the team can quickly find circular dependencies and prevent cascading failures.

Why force-directed layout is the foundation of Network Graph?

Force-directed layout physically simulates nodes: they repel each other, and links attract. The result is a natural arrangement where connected nodes are close and unconnected ones are far apart. This is the key to readability. Without such an algorithm, the graph turns into chaotic spaghetti.

Which library to choose for graph visualization?

Library Performance (nodes) Complexity When to choose
D3 Force (recommended) up to 10k High Full control needed, custom renders
react-force-graph up to 5k (2D/3D) Medium React project, quick start
Sigma.js 50k+ Medium Large graphs with WebGL
Cytoscape.js up to 10k Low Biological/medical data

D3 Force vs Sigma.js: comparative analysis

Sigma.js processes 50k nodes 5 times faster than D3 Force due to WebGL. For projects with >10k nodes, Sigma.js is the optimal choice, although D3 Force offers more flexibility in customization. Sigma.js official documentation confirms that the library renders up to 100k nodes on an ordinary laptop.

Comparison of 2D and 3D rendering

Parameter 2D 3D
Performance Higher (fewer calculations) Lower (Z-coordinate)
Intuitiveness Familiar view Requires orbit control
Application Service dependencies, social networks Geodata, molecular structures

The choice between 2D and 3D depends on the task. For service dependency visualization, 2D is usually sufficient. 3D is needed for geodata or molecular structures. The visualization method is determined by the data type and interactivity requirements.

How we do it: stack and case study

For visualizing microservice dependencies, we use a JS graph based on react-force-graph. We prefer react-force-graph — it provides a ready React component with WebGL acceleration. For a large project from our practice (5000+ nodes), we used Sigma.js: raw data loaded via graphology, layout configured via force-atlas2.

In practice, we often use a hybrid approach: at the exploration stage, we build a prototype on D3 Force, then transfer to Sigma.js or react-force-graph for production. This allows quick hypothesis validation without deep optimization. For a fintech client, we developed a transaction monitoring graph where each node is an account and links are transfers. The graph updated in real time and withstood up to 1000 changes per second. Another client reduced dependency search time from 4 hours to 15 minutes, saving over 150,000 rubles monthly. If you want to assess whether your stack is suitable for Network Graph, contact us for a free audit.

Example of a basic graph with react-force-graph

npm install react-force-graph-2d 
import ForceGraph2D from 'react-force-graph-2d'; import { useCallback, useRef, useState } from 'react'; interface Node { id: string; name: string; type: 'service' | 'database' | 'external'; group: number; } interface Link { source: string; target: string; label?: string; weight?: number; } function ServiceDependencyGraph({ nodes, links }) { const graphRef = useRef(); const [selectedNode, setSelectedNode] = useState(null); const [hoveredNode, setHoveredNode] = useState(null); // Highlight links of selected node const highlightedLinks = useMemo(() => { if (!selectedNode) return new Set(); return new Set( links.filter(l => l.source === selectedNode.id || l.target === selectedNode.id) .map(l => `${l.source}-${l.target}`) ); }, [selectedNode, links]); const nodeColor = useCallback((node) => { const colors = { service: '#3b82f6', database: '#22c55e', external: '#f59e0b' }; if (selectedNode && node.id !== selectedNode.id) { const isRelated = links.some(l => (l.source === selectedNode.id && l.target === node.id) || (l.target === selectedNode.id && l.source === node.id) ); return isRelated ? colors[node.type] : '#d1d5db'; } return colors[node.type] ?? '#6b7280'; }, [selectedNode, links]); const nodeCanvasObject = useCallback((node, ctx, globalScale) => { const label = node.name; const fontSize = 12 / globalScale; const r = 6; // Node ctx.beginPath(); ctx.arc(node.x, node.y, r, 0, 2 * Math.PI); ctx.fillStyle = nodeColor(node); ctx.fill(); if (selectedNode?.id === node.id) { ctx.strokeStyle = '#1d4ed8'; ctx.lineWidth = 2 / globalScale; ctx.stroke(); } // Label ctx.font = `${fontSize}px Sans-Serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#374151'; ctx.fillText(label, node.x, node.y + r + fontSize); }, [nodeColor, selectedNode]); const linkColor = useCallback((link) => { const key = `${link.source.id ?? link.source}-${link.target.id ?? link.target}`; return highlightedLinks.has(key) ? '#3b82f6' : '#e5e7eb'; }, [highlightedLinks]); return ( <div className="relative border border-gray-200 rounded-xl overflow-hidden" style={{ height: 600 }}> <ForceGraph2D ref={graphRef} graphData={{ nodes, links }} nodeId="id" nodeLabel="name" nodeCanvasObject={nodeCanvasObject} nodeCanvasObjectMode={() => 'replace'} linkColor={linkColor} linkWidth={link => (link.weight ?? 1) * 0.5} linkDirectionalArrowLength={3} linkDirectionalArrowRelPos={1} linkLabel="label" onNodeClick={(node) => { setSelectedNode(prev => prev?.id === node.id ? null : node); }} onNodeHover={setHoveredNode} onBackgroundClick={() => setSelectedNode(null)} d3AlphaDecay={0.02} d3VelocityDecay={0.3} warmupTicks={50} cooldownTicks={200} /> {/* Info panel */} {selectedNode && ( <div className="absolute top-4 right-4 bg-white border rounded-lg shadow-lg p-4 w-64"> <h3 className="font-semibold text-gray-800">{selectedNode.name}</h3> <p className="text-sm text-gray-500 capitalize">{selectedNode.type}</p> <div className="mt-3"> <p className="text-xs text-gray-500">Incoming links:</p> <ul className="text-sm"> {links.filter(l => (l.target.id ?? l.target) === selectedNode.id) .map(l => <li key={l.source.id ?? l.source}>← {l.source.name ?? l.source}</li>)} </ul> </div> </div> )} {/* Legend */} <div className="absolute bottom-4 left-4 bg-white/90 rounded-lg p-3 text-xs"> <div className="flex items-center gap-2 mb-1"> <div className="w-3 h-3 rounded-full bg-blue-500" /> Service </div> <div className="flex items-center gap-2 mb-1"> <div className="w-3 h-3 rounded-full bg-green-500" /> Database </div> <div className="flex items-center gap-2"> <div className="w-3 h-3 rounded-full bg-yellow-500" /> External API </div> </div> </div> ); } 

3D graph

import ForceGraph3D from 'react-force-graph-3d'; function Graph3D({ data }) { return ( <ForceGraph3D graphData={data} nodeAutoColorBy="group" nodeLabel="name" linkDirectionalParticles={2} linkDirectionalParticleSpeed={0.006} backgroundColor="#0f172a" /> ); } 

Large graphs with Sigma.js

npm install sigma graphology @react-sigma/core 

Sigma.js renders via WebGL — handles 50k+ nodes. Use Sigma.js if maximum performance is needed.

import { SigmaContainer, useLoadGraph, useRegisterEvents } from '@react-sigma/core'; import Graph from 'graphology'; function LargeGraph({ graphData }) { return ( <SigmaContainer style={{ height: 600 }} settings={{ nodeProgramClasses: {} }}> <GraphLoader data={graphData} /> </SigmaContainer> ); } 

Process of work

  1. Analytics — study data sources for graph visualization, clarify interactivity requirements (zoom, highlight, filters).
  2. Design — choose layout (force-directed, circular, hierarchical), define color scheme and sizes.
  3. Development — implement component, optimize rendering (node virtualization, LOD).
  4. Testing — check on real data, measure FPS, fix bugs.
  5. Deployment — embed into the site, configure CDN for heavy assets.

Deadlines and cost

Deadlines: from 1.5 weeks (basic graph) to 1 month (comprehensive solution with Sigma.js + 3D). Cost is calculated individually after data audit. Basic graph development starts from 150,000 rubles ($1,800). Typical project budget: from $2,000 to $10,000 depending on complexity and data volume. As practice shows, the graph pays for itself within a month of implementation by reducing error search time. For example, one client reduced dependency search time from 4 hours to 15 minutes, saving over 150,000 rubles per month. We guarantee stable operation on mobile devices and older browsers. Order an audit — we will determine the optimal solution for your data.

What is included in the work

  • Adaptation of the graph to your stack (React, Vue, Angular)
  • Source code as an npm package or component
  • Documentation: API reference and customization guide
  • Access: source code repository (GitHub)
  • Training: 1-hour online session for your team
  • Support: 2 weeks of technical support including bug fixes and consultation
  • Test integration with your data
Typical mistakes when developing a Network Graph
  • Ignoring performance: without WebGL, the graph lags at 1000 nodes.
  • No event throttling: each hover triggers a repaint.
  • Too small click target areas — user cannot select a node.

We have many years of experience in data visualization — we have implemented over 50 projects, including graphs for banks and logistics systems. Order a graph development tailored to your data, or get a consultation from an engineer. Optimization via WebGL and clustering allows rendering graphs with 100,000+ nodes on an ordinary laptop. Check if your current stack is suitable for Network Graph by ordering a free audit. Contact us to discuss your project.