Visualize 3D graph in 2D

by Rajesh Danabal

HTML

<canvas id="canvas"></canvas>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const numNodes = 80;
const radius = 250;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;

// Generate nodes with simulated 3D depth
const nodes = d3.range(numNodes).map((i) => {
    const angle = (i / numNodes) * 2 * Math.PI;
    const depth = Math.random(); // Simulated Z-depth (0: far, 1: near)
    return {
        id: i,
        x: centerX + radius * Math.cos(angle) * (0.8 + 0.2 * depth),
        y: centerY + radius * Math.sin(angle) * (0.8 + 0.2 * depth),
        size: 3 + 6 * depth, // Larger nodes appear closer
        opacity: 0.3 + 0.7 * depth, // Fainter nodes appear farther
        depth
    };
});

// Generate random links with fading effect
const links = [];
for (let i = 0; i < numNodes; i++) {
    for (let j = i + 1; j < numNodes; j++) {
        if (Math.random() < 0.1) { // 10% chance of connection
            links.push({ source: nodes[i], target: nodes[j] });
        }
    }
}

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

    // Sort nodes by depth to ensure proper layering
    nodes.sort((a, b) => a.depth - b.depth);

    // Draw edges with depth-based opacity
    links.forEach((link) => {
        ctx.strokeStyle = `rgba(100, 100, 100, ${0.2 + 0.4 * (link.source.depth + link.target.depth) / 2})`;
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(link.source.x, link.source.y);
        ctx.lineTo(link.target.x, link.target.y);
        ctx.stroke();
    });

    // Draw nodes with depth-based size & opacity
    nodes.forEach((node) => {
        ctx.beginPath();
        ctx.arc(node.x, node.y, node.size, 0, 2 * Math.PI);
        ctx.fillStyle = `rgba(30, 144, 255, ${node.opacity})`;
        ctx.fill();
    });

   ...