JSFiddle - React, Tailwind, and code Playground

by Rajesh Danabal

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Network Graph with Clustering</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
</head>
<body>
    <canvas id="networkCanvas"></canvas>

</body>
</html>

CSS

body { margin: 0; overflow: hidden; }
canvas { display: block; }

JavaScript

document.addEventListener("DOMContentLoaded", function () {
    const canvas = document.getElementById("networkCanvas");
    const ctx = canvas.getContext("2d");

    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const numNodes = 100;
    const numClusters = 5;
    const clusterCenters = [];
    const hullOffset = 20;

    // Generate cluster centers
    for (let i = 0; i < numClusters; i++) {
        clusterCenters.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height
        });
    }

    // Create nodes with clusters
    const nodes = [];
    for (let i = 0; i < numNodes; i++) {
        const cluster = i % numClusters;
        nodes.push({
            id: i,
            cluster,
            x: clusterCenters[cluster].x + Math.random() * 50 - 25,
            y: clusterCenters[cluster].y + Math.random() * 50 - 25
        });
    }

    // Create links between same-cluster nodes
    const links = [];
    nodes.forEach((node, i) => {
        for (let j = 0; j < 3; j++) {
            const target = Math.floor(Math.random() * numNodes);
            if (i !== target && nodes[target].cluster === node.cluster) {
                links.push({ source: node, target: nodes[target] });
            }
        }
    });

    // D3 force simulation
    const simulation = d3.forceSimulation(nodes)
        .force("link", d3.forceLink(links).id(d => d.id).distance(50))
        .force("charge", d3.forceManyBody().strength(-10))
        .force("collision", d3.forceCollide(15))
        .force("x", d3.forceX(d => clusterCenters[d.cluster].x).strength(0.1))
        .force("y", d3.forceY(d => clusterCenters[d.cluster].y).strength(0.1))
        .on("tick", draw);

    function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // Group nodes by cluster
        const clusters = {};
        nodes.forEach(node => {
            if (!clusters[node.cluster]) {
               ...