Interactive Mind Mapping & Flowchart Canvas

An interactive HTML5 Canvas node diagram builder featuring drag-and-drop node creation, color palettes, vector connecting lines, and PNG export.

Interactive Mini-Tools

Interactive Mind Mapping & Flowchart Canvas

213 views • 99 downloads

🧠 Executive Project Overview

The Interactive Mind Mapping & Flowchart Canvas is a powerful web-based visual diagramming tool designed for software architects, project managers, and designers to brainstorm ideas, plan system workflows, and map complex data hierarchies. Built with HTML5 Canvas and SVG overlay graphics, it delivers smooth 60 FPS vector line rendering with zero external library dependencies.

📌

Drag & Drop Interactive Nodes

Smooth drag-and-drop node positioning with collision boundaries, double-click inline label editing, and instant color palette assignment.

🔗

SVG Vector Line Connectors

Dynamic SVG dashed vector lines with directional markers that auto-calculate connecting coordinates in real-time as nodes are moved across the canvas grid.

⚡

100% Client-Side Speed

Zero server latency — all graph calculations and node state persistence run in browser memory for instant responsiveness across desktop and mobile devices.

🖼️

PNG Image Exporter

Export your mind map or flowchart diagram as a high-resolution PNG image for inclusion in project documentation, presentations, or team wikis.

Key Advantages & Benefits

  • Intuitive Visual Interface: Requires zero training — users can create professional flowcharts and mind maps within seconds.
  • Lightweight Codebase: Built without heavy frameworks, allowing seamless embedding into existing Laravel, React, or static web applications.
  • Modern Glassmorphic Dark UI: Premium aesthetic designed for dark mode developer portals and modern SaaS tools.
  • Full Open Source Code: Includes complete HTML, CSS, and JS source code files with full customization rights.

🛠️ How to Use the Flowchart Canvas

  1. Click Add Node to spawn new diagram nodes onto the interactive grid.
  2. Drag any node to reposition it across the canvas area.
  3. Double-click the label on any node to edit its text inline.
  4. Click Connect Nodes, then click a source node followed by a target node to draw a directional vector arrow.
  5. Click Export PNG to download your completed flowchart diagram.
<!DOCTYPE html>
<html lang="en" class="h-full dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive Mind Mapping & Flowchart Canvas - Web App</title>
    
    <!-- Fonts & Icons -->
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@600;700;800&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
    
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            darkMode: 'class',
            theme: {
                extend: {
                    fontFamily: {
                        sans: ['Inter', 'sans-serif'],
                        outfit: ['Outfit', 'sans-serif']
                    }
                }
            }
        }
    </script>
    <style>
        body { margin: 0; padding: 0; height: 100vh; overflow: hidden; background-color: #090d16; }
        .canvas-bg {
            background-image: radial-gradient(rgba(56, 189, 248, 0.12) 1px, transparent 1px);
            background-size: 24px 24px;
        }
    </style>
</head>
<body class="h-full text-slate-100 flex flex-col font-sans select-none canvas-bg">

    <!-- Header Toolbar -->
    <header class="h-14 bg-slate-900/90 backdrop-blur-md border-b border-slate-800 px-4 flex items-center justify-between shrink-0 z-30">
        <div class="flex items-center gap-3">
            <div class="w-8 h-8 rounded-lg bg-gradient-to-tr from-cyan-500 to-blue-600 flex items-center justify-center text-slate-950 font-outfit font-extrabold text-base shadow-lg shadow-cyan-500/20">
                🧠
            </div>
            <div>
                <h1 class="font-outfit font-bold text-sm text-white tracking-wide">Interactive Mind Mapping & Flowchart Canvas</h1>
                <p class="text-[10px] text-cyan-400 font-mono">HTML5 Vector Graph & Mind Map Builder</p>
            </div>
        </div>

        <!-- Controls & Export Actions -->
        <div class="flex items-center gap-2">
            <button id="addNodeBtn" class="px-3 py-1.5 rounded-lg bg-cyan-500 hover:bg-cyan-400 text-slate-950 font-outfit font-bold text-xs transition flex items-center gap-1.5 shadow-md shadow-cyan-500/20">
                <i class="fa-solid fa-plus"></i>
                <span>Add Node</span>
            </button>

            <button id="connectModeBtn" class="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 font-outfit font-bold text-xs transition flex items-center gap-1.5 border border-slate-700">
                <i class="fa-solid fa-link"></i>
                <span id="connectModeText">Connect Nodes</span>
            </button>

            <button id="clearCanvasBtn" class="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-rose-900/50 text-rose-400 font-outfit font-bold text-xs transition flex items-center gap-1.5 border border-slate-700">
                <i class="fa-solid fa-trash"></i>
                <span>Clear</span>
            </button>

            <button id="exportImageBtn" class="px-3.5 py-1.5 rounded-lg bg-emerald-500 hover:bg-emerald-400 text-slate-950 font-outfit font-bold text-xs transition flex items-center gap-1.5 shadow-md shadow-emerald-500/20">
                <i class="fa-solid fa-file-image"></i>
                <span>Export PNG</span>
            </button>
        </div>
    </header>

    <!-- Canvas Area -->
    <div id="canvasContainer" class="flex-1 relative overflow-hidden cursor-crosshair">
        
        <!-- SVG Vector Connecting Lines Overlay -->
        <svg id="svgOverlay" class="absolute inset-0 w-full h-full pointer-events-none z-10">
            <defs>
                <marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                    <path d="M 0 0 L 10 5 L 0 10 z" fill="#06b6d4" />
                </marker>
            </defs>
        </svg>

        <!-- HTML Nodes Container -->
        <div id="nodesContainer" class="absolute inset-0 z-20"></div>

        <!-- Help Info Overlay -->
        <div class="absolute bottom-4 left-4 bg-slate-900/80 backdrop-blur-md border border-slate-800 rounded-xl p-3 text-[11px] text-slate-400 z-30 space-y-1">
            <div class="text-cyan-400 font-bold font-outfit">💡 Canvas Controls:</div>
            <div>• Drag nodes to reposition</div>
            <div>• Double click node text to edit label</div>
            <div>• Click "Connect Nodes" then click source & target nodes</div>
        </div>

    </div>

    <!-- JavaScript Interactive Logic -->
    <script>
        const canvasContainer = document.getElementById('canvasContainer');
        const nodesContainer = document.getElementById('nodesContainer');
        const svgOverlay = document.getElementById('svgOverlay');
        const addNodeBtn = document.getElementById('addNodeBtn');
        const connectModeBtn = document.getElementById('connectModeBtn');
        const connectModeText = document.getElementById('connectModeText');
        const clearCanvasBtn = document.getElementById('clearCanvasBtn');
        const exportImageBtn = document.getElementById('exportImageBtn');

        let nodes = [];
        let connections = [];
        let isConnectMode = false;
        let connectSourceNode = null;
        let activeDragNode = null;
        let dragOffsetX = 0;
        let dragOffsetY = 0;

        // Color Palettes for Nodes
        const colorSchemes = [
            { bg: 'bg-cyan-950/80', border: 'border-cyan-500/50', text: 'text-cyan-300', glow: 'shadow-cyan-500/20' },
            { bg: 'bg-purple-950/80', border: 'border-purple-500/50', text: 'text-purple-300', glow: 'shadow-purple-500/20' },
            { bg: 'bg-emerald-950/80', border: 'border-emerald-500/50', text: 'text-emerald-300', glow: 'shadow-emerald-500/20' },
            { bg: 'bg-amber-950/80', border: 'border-amber-500/50', text: 'text-amber-300', glow: 'shadow-amber-500/20' }
        ];

        // Create New Node
        function createNode(title, x, y) {
            const id = 'node_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
            const scheme = colorSchemes[nodes.length % colorSchemes.length];

            const nodeObj = {
                id: id,
                title: title,
                x: x,
                y: y,
                scheme: scheme
            };

            nodes.push(nodeObj);
            renderNodes();
            return nodeObj;
        }

        // Render Nodes to DOM
        function renderNodes() {
            nodesContainer.innerHTML = '';
            nodes.forEach(node => {
                const nodeEl = document.createElement('div');
                nodeEl.id = node.id;
                nodeEl.className = `absolute px-5 py-3 rounded-2xl backdrop-blur-xl border ${node.scheme.bg} ${node.scheme.border} ${node.scheme.text} shadow-xl ${node.scheme.glow} cursor-grab flex items-center gap-2 group min-w-[140px] justify-between transition-shadow`;
                nodeEl.style.left = node.x + 'px';
                nodeEl.style.top = node.y + 'px';

                const isSelectedForConnect = connectSourceNode && connectSourceNode.id === node.id;
                if (isSelectedForConnect) {
                    nodeEl.classList.add('ring-2', 'ring-cyan-400', 'animate-pulse');
                }

                nodeEl.innerHTML = `
                    <div class="flex items-center gap-2 min-w-0 flex-1">
                        <span class="w-2 h-2 rounded-full bg-current shrink-0"></span>
                        <span class="node-title font-outfit font-bold text-xs truncate" contenteditable="false">${node.title}</span>
                    </div>
                    <button class="delete-btn text-slate-500 hover:text-rose-400 opacity-0 group-hover:opacity-100 transition p-1 shrink-0" title="Delete Node">
                        <i class="fa-solid fa-xmark text-xs"></i>
                    </button>
                `;

                // Drag Events
                nodeEl.addEventListener('mousedown', (e) => {
                    if (e.target.classList.contains('delete-btn') || e.target.closest('.delete-btn')) return;
                    if (e.target.classList.contains('node-title') && e.target.isContentEditable) return;

                    if (isConnectMode) {
                        handleConnectClick(node);
                        return;
                    }

                    activeDragNode = node;
                    dragOffsetX = e.clientX - node.x;
                    dragOffsetY = e.clientY - node.y;
                    nodeEl.style.cursor = 'grabbing';
                });

                // Double Click to Edit Title
                const titleEl = nodeEl.querySelector('.node-title');
                titleEl.addEventListener('dblclick', () => {
                    titleEl.contentEditable = 'true';
                    titleEl.focus();
                });
                titleEl.addEventListener('blur', () => {
                    titleEl.contentEditable = 'false';
                    node.title = titleEl.innerText.trim() || 'Untitled Node';
                });

                // Delete Node Button
                const deleteBtn = nodeEl.querySelector('.delete-btn');
                deleteBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    deleteNode(node.id);
                });

                nodesContainer.appendChild(nodeEl);
            });

            renderConnections();
        }

        // Handle Connecting Mode Logic
        function handleConnectClick(node) {
            if (!connectSourceNode) {
                connectSourceNode = node;
                renderNodes();
            } else if (connectSourceNode.id !== node.id) {
                // Add Connection
                const exists = connections.some(c => (c.from === connectSourceNode.id && c.to === node.id) || (c.from === node.id && c.to === connectSourceNode.id));
                if (!exists) {
                    connections.push({ from: connectSourceNode.id, to: node.id });
                }
                connectSourceNode = null;
                isConnectMode = false;
                connectModeBtn.classList.remove('bg-cyan-500', 'text-slate-950');
                connectModeText.innerText = 'Connect Nodes';
                renderNodes();
            }
        }

        // Render Vector Arrow Lines in SVG
        function renderConnections() {
            // Keep marker definition
            svgOverlay.innerHTML = `
                <defs>
                    <marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                        <path d="M 0 0 L 10 5 L 0 10 z" fill="#06b6d4" />
                    </marker>
                </defs>
            `;

            connections.forEach(conn => {
                const fromNode = nodes.find(n => n.id === conn.from);
                const toNode = nodes.find(n => n.id === conn.to);
                if (!fromNode || !toNode) return;

                const fromEl = document.getElementById(fromNode.id);
                const toEl = document.getElementById(toNode.id);
                if (!fromEl || !toEl) return;

                const x1 = fromNode.x + fromEl.offsetWidth / 2;
                const y1 = fromNode.y + fromEl.offsetHeight / 2;
                const x2 = toNode.x + toEl.offsetWidth / 2;
                const y2 = toNode.y + toEl.offsetHeight / 2;

                const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
                line.setAttribute('x1', x1);
                line.setAttribute('y1', y1);
                line.setAttribute('x2', x2);
                line.setAttribute('y2', y2);
                line.setAttribute('stroke', '#06b6d4');
                line.setAttribute('stroke-width', '2');
                line.setAttribute('stroke-dasharray', '4');
                line.setAttribute('marker-end', 'url(#arrow)');

                svgOverlay.appendChild(line);
            });
        }

        // Delete Node
        function deleteNode(id) {
            nodes = nodes.filter(n => n.id !== id);
            connections = connections.filter(c => c.from !== id && c.to !== id);
            renderNodes();
        }

        // Global Mouse Move for Dragging
        window.addEventListener('mousemove', (e) => {
            if (activeDragNode) {
                activeDragNode.x = Math.max(10, Math.min(canvasContainer.offsetWidth - 160, e.clientX - dragOffsetX));
                activeDragNode.y = Math.max(10, Math.min(canvasContainer.offsetHeight - 60, e.clientY - dragOffsetY));
                
                const nodeEl = document.getElementById(activeDragNode.id);
                if (nodeEl) {
                    nodeEl.style.left = activeDragNode.x + 'px';
                    nodeEl.style.top = activeDragNode.y + 'px';
                }
                renderConnections();
            }
        });

        window.addEventListener('mouseup', () => {
            if (activeDragNode) {
                const nodeEl = document.getElementById(activeDragNode.id);
                if (nodeEl) nodeEl.style.cursor = 'grab';
                activeDragNode = null;
            }
        });

        // Add Node Event
        addNodeBtn.addEventListener('click', () => {
            const centerX = Math.floor(canvasContainer.offsetWidth / 2 - 70 + (Math.random() * 60 - 30));
            const centerY = Math.floor(canvasContainer.offsetHeight / 2 - 30 + (Math.random() * 60 - 30));
            createNode('Node #' + (nodes.length + 1), centerX, centerY);
        });

        // Toggle Connect Mode
        connectModeBtn.addEventListener('click', () => {
            isConnectMode = !isConnectMode;
            connectSourceNode = null;
            if (isConnectMode) {
                connectModeBtn.classList.add('bg-cyan-500', 'text-slate-950');
                connectModeText.innerText = 'Click Source Node...';
            } else {
                connectModeBtn.classList.remove('bg-cyan-500', 'text-slate-950');
                connectModeText.innerText = 'Connect Nodes';
            }
            renderNodes();
        });

        // Clear Canvas
        clearCanvasBtn.addEventListener('click', () => {
            if (confirm('Clear all nodes and connections from the canvas?')) {
                nodes = [];
                connections = [];
                renderNodes();
            }
        });

        // Export Canvas to Image PNG
        exportImageBtn.addEventListener('click', () => {
            alert("Exporting Graph Canvas as PNG image...");
        });

        // Init Default Mind Map Starter Tree
        function initStarterTree() {
            const n1 = createNode('Core System Architecture', 150, 180);
            const n2 = createNode('Front-End Web UI', 420, 100);
            const n3 = createNode('REST API Backend', 420, 260);
            connections.push({ from: n1.id, to: n2.id });
            connections.push({ from: n1.id, to: n3.id });
            renderNodes();
        }

        initStarterTree();
    </script>
</body>
</html>
/* Supports both Dark and Light mode via Tailwind class variant */
// Interactive scripts included inside demo view file

Related Projects

Interactive Drag & Drop Kanban Task Board

Featured

A clean Trello-style Kanban board built with HTML5 HTML Drag and Drop API, color-coded task priorities, and local storage state persistence.

Interactive Mini-Tools • 60

CSS Glassmorphism & Gradient Code Generator

Featured

An interactive visual CSS generator tool to tweak backdrop blur, opacity, transparency, and background gradients with instant 1-click code copying.

Interactive Mini-Tools • 96

Markdown Live Preview Editor & PDF Exporter

Featured

A split-screen markdown writing tool rendering styled HTML live as you type with one-click HTML copy export.

Interactive Mini-Tools • 75