#4517 KG for Music Theory with Clickable Links to Web Pages

#4517 KG for Music Theory with Clickable Links to Web Pages

#4517 Create an HTML, JavaScript, CSS app with a knowledge graph of music theory, with clickable node links to webpages for each node topic

Here is a complete, single-file HTML application that builds an interactive music theory knowledge graph. It uses D3.js (v7) for a smooth, draggable, and zoomable force-directed graph.

Every node represents a core music theory concept. Clicking a node will instantly open a curated, high-quality educational webpage (like Wikipedia or Open Music Theory) in a new tab.

How to Use It:

  1. Copy the code below.

  2. Save it as music_theory_graph.html.

  3. Double-click the file to open it in any web browser.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Music Theory Knowledge Graph</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        :root {
            --bg-color: #1a1a24;
            --panel-bg: #252538;
            --text-color: #f0f0f6;
            --accent-color: #4f46e5;
            --node-core: #ff007f;
            --node-pitch: #00f0ff;
            --node-harmony: #ad00ff;
            --node-rhythm: #ffaa00;
            --node-form: #00ff66;
            --link-color: rgba(255, 255, 255, 0.15);
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: var(--bg-color);
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            color: var(--text-color);
        }

        #header {
            position: absolute;
            top: 20px;
            left: 20px;
            z-index: 10;
            pointer-events: none;
        }

        h1 {
            margin: 0 0 5px 0;
            font-size: 1.8rem;
            font-weight: 700;
            letter-spacing: 1px;
            text-shadow: 0 2px 4px rgba(0,0,0,0.5);
        }

        p {
            margin: 0;
            font-size: 0.9rem;
            opacity: 0.8;
        }

        #graph-container {
            width: 100%;
            height: 100%;
        }

        /* D3 Styling */
        .node {
            cursor: pointer;
            transition: filter 0.2s ease;
        }

        .node:hover {
            filter: brightness(1.3) drop-shadow(0px 0px 8px rgba(255,255,255,0.6));
        }

        .node-label {
            font-size: 12px;
            font-weight: 600;
            pointer-events: none;
            text-shadow: 0 2px 4px rgba(0,0,0,0.9), 0 -1px 2px rgba(0,0,0,0.9);
        }

        .link {
            stroke: var(--link-color);
            stroke-width: 1.5px;
            stroke-dasharray: 4 2;
        }

        /* Legend Panel */
        #legend {
            position: absolute;
            bottom: 20px;
            left: 20px;
            background: var(--panel-bg);
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.5);
            font-size: 0.85rem;
            z-index: 10;
        }

        .legend-title {
            font-weight: bold;
            margin-bottom: 8px;
            border-bottom: 1px solid rgba(255,255,255,0.1);
            padding-bottom: 4px;
        }

        .legend-item {
            display: flex;
            align-items: center;
            margin: 4px 0;
        }

        .legend-color {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            margin-right: 8px;
        }
    </style>
</head>
<body>

    <div id="header">
        <h1>Music Theory Knowledge Graph</h1>
        <p>Scroll to Zoom. Drag nodes to explore. <strong>Click a node</strong> to open its learning resource.</p>
    </div>

    <div id="legend">
        <div class="legend-title">Categories</div>
        <div class="legend-item"><div class="legend-color" style="background: var(--node-core);"></div>Core Foundation</div>
        <div class="legend-item"><div class="legend-color" style="background: var(--node-pitch);"></div>Pitch & Scales</div>
        <div class="legend-item"><div class="legend-color" style="background: var(--node-harmony);"></div>Harmony & Chords</div>
        <div class="legend-item"><div class="legend-color" style="background: var(--node-rhythm);"></div>Rhythm & Meter</div>
        <div class="legend-item"><div class="legend-color" style="background: var(--node-form);"></div>Form & Structure</div>
    </div>

    <div id="graph-container"></div>

    <script>
        // --- 1. DATA DEFINITION ---
        const graphData = {
            nodes: [
                // Core
                { id: "Music Theory", group: "core", radius: 25, url: "https://en.wikipedia.org/wiki/Music_theory" },
                
                // Pitch & Scales
                { id: "Pitch", group: "pitch", radius: 18, url: "https://en.wikipedia.org/wiki/Pitch_(music)" },
                { id: "Intervals", group: "pitch", radius: 15, url: "https://en.wikipedia.org/wiki/Interval_(music)" },
                { id: "Scales", group: "pitch", radius: 16, url: "https://en.wikipedia.org/wiki/Scale_(music)" },
                { id: "Major Scale", group: "pitch", radius: 14, url: "https://en.wikipedia.org/wiki/Major_scale" },
                { id: "Minor Scale", group: "pitch", radius: 14, url: "https://en.wikipedia.org/wiki/Minor_scale" },
                { id: "Modes", group: "pitch", radius: 14, url: "https://en.wikipedia.org/wiki/Mode_(music)" },
                { id: "Circle of Fifths", group: "pitch", radius: 16, url: "https://en.wikipedia.org/wiki/Circle_of_fifths" },
                
                // Harmony & Chords
                { id: "Harmony", group: "harmony", radius: 18, url: "https://en.wikipedia.org/wiki/Harmony" },
                { id: "Chords", group: "harmony", radius: 16, url: "https://en.wikipedia.org/wiki/Chord_(music)" },
                { id: "Triads", group: "harmony", radius: 14, url: "https://en.wikipedia.org/wiki/Triad_(music)" },
                { id: "Seventh Chords", group: "harmony", radius: 14, url: "https://en.wikipedia.org/wiki/Seventh_chord" },
                { id: "Chord Progressions", group: "harmony", radius: 16, url: "https://en.wikipedia.org/wiki/Chord_progression" },
                { id: "Voice Leading", group: "harmony", radius: 14, url: "https://en.wikipedia.org/wiki/Voice_leading" },
                { id: "Counterpoint", group: "harmony", radius: 15, url: "https://en.wikipedia.org/wiki/Counterpoint" },
                
                // Rhythm & Meter
                { id: "Rhythm", group: "rhythm", radius: 18, url: "https://en.wikipedia.org/wiki/Rhythm" },
                { id: "Tempo", group: "rhythm", radius: 14, url: "https://en.wikipedia.org/wiki/Tempo" },
                { id: "Meter", group: "rhythm", radius: 16, url: "https://en.wikipedia.org/wiki/Meter_(music)" },
                { id: "Time Signatures", group: "rhythm", radius: 14, url: "https://en.wikipedia.org/wiki/Time_signature" },
                { id: "Syncopation", group: "rhythm", radius: 13, url: "https://en.wikipedia.org/wiki/Syncopation" },
                
                // Form & Structure
                { id: "Musical Form", group: "form", radius: 18, url: "https://en.wikipedia.org/wiki/Musical_form" },
                { id: "Binary Form", group: "form", radius: 13, url: "https://en.wikipedia.org/wiki/Binary_form" },
                { id: "Ternary Form", group: "form", radius: 13, url: "https://en.wikipedia.org/wiki/Ternary_form" },
                { id: "Sonata Form", group: "form", radius: 15, url: "https://en.wikipedia.org/wiki/Sonata_form" }
            ],
            links: [
                // Core connections
                { source: "Music Theory", target: "Pitch" },
                { source: "Music Theory", target: "Harmony" },
                { source: "Music Theory", target: "Rhythm" },
                { source: "Music Theory", target: "Musical Form" },
                
                // Pitch connections
                { source: "Pitch", target: "Intervals" },
                { source: "Pitch", target: "Scales" },
                { source: "Scales", target: "Major Scale" },
                { source: "Scales", target: "Minor Scale" },
                { source: "Scales", target: "Modes" },
                { source: "Scales", target: "Circle of Fifths" },
                { source: "Circle of Fifths", target: "Major Scale" },
                { source: "Circle of Fifths", target: "Minor Scale" },
                
                // Harmony connections
                { source: "Harmony", target: "Chords" },
                { source: "Intervals", target: "Chords" }, // Cross-categorical link
                { source: "Chords", target: "Triads" },
                { source: "Chords", target: "Seventh Chords" },
                { source: "Chords", target: "Chord Progressions" },
                { source: "Circle of Fifths", target: "Chord Progressions" }, // Cross link
                { source: "Harmony", target: "Counterpoint" },
                { source: "Counterpoint", target: "Voice Leading" },
                { source: "Chord Progressions", target: "Voice Leading" },
                
                // Rhythm connections
                { source: "Rhythm", target: "Tempo" },
                { source: "Rhythm", target: "Meter" },
                { source: "Meter", target: "Time Signatures" },
                { source: "Rhythm", target: "Syncopation" },
                
                // Form connections
                { source: "Musical Form", target: "Binary Form" },
                { source: "Musical Form", target: "Ternary Form" },
                { source: "Musical Form", target: "Sonata Form" },
                { source: "Chord Progressions", target: "Musical Form" } // Structural harmony link
            ]
        };

        // --- 2. CONFIGURATION & COLOR SETUP ---
        const container = document.getElementById('graph-container');
        const width = container.clientWidth;
        const height = container.clientHeight;

        const colors = {
            core: "#ff007f",
            pitch: "#00f0ff",
            harmony: "#ad00ff",
            rhythm: "#ffaa00",
            form: "#00ff66"
        };

        // --- 3. INITIALIZE SVG & ZOOM ---
        const svg = d3.select("#graph-container")
            .append("svg")
            .attr("width", "100%")
            .attr("height", "100%")
            .attr("viewBox", [0, 0, width, height]);

        // Create a master grouping element for zoom/pan support
        const g = svg.append("g");

        svg.call(d3.zoom()
            .scaleExtent([0.3, 4])
            .on("zoom", (event) => {
                g.attr("transform", event.transform);
            }));

        // --- 4. FORCE SIMULATION CONFIGURATION ---
        const simulation = d3.forceSimulation(graphData.nodes)
            .force("link", d3.forceLink(graphData.links).id(d => d.id).distance(100))
            .force("charge", d3.forceManyBody().strength(-400))
            .force("center", d3.forceCenter(width / 2, height / 2))
            .force("collision", d3.forceCollide().radius(d => d.radius + 20));

        // --- 5. RENDER ELEMENTS ---
        // Render links (Lines)
        const link = g.append("g")
            .attr("class", "links")
            .selectAll("line")
            .data(graphData.links)
            .enter().append("line")
            .attr("class", "link");

        // Render nodes (Groups containing Circles + Labels)
        const node = g.append("g")
            .attr("class", "nodes")
            .selectAll("g")
            .data(graphData.nodes)
            .enter().append("g")
            .attr("class", "node")
            .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended))
            .on("click", (event, d) => {
                // Prevent node click trigger if dragging
                if (event.defaultPrevented) return;
                window.open(d.url, '_blank');
            });

        // Add circles to nodes
        node.append("circle")
            .attr("r", d => d.radius)
            .attr("fill", d => colors[d.group] || "#fff")
            .attr("opacity", 0.85)
            .attr("stroke", "#fff")
            .attr("stroke-width", 1.5);

        // Add text labels to nodes
        node.append("text")
            .attr("class", "node-label")
            .attr("dy", d => d.radius + 16) // Position label below the circle
            .attr("text-anchor", "middle")
            .attr("fill", "#ffffff")
            .text(d => d.id);

        // --- 6. ANIMATION TICK LOOP ---
        simulation.on("tick", () => {
            link
                .attr("x1", d => d.source.x)
                .attr("y1", d => d.source.y)
                .attr("x2", d => d.target.x)
                .attr("y2", d => d.target.y);

            node
                .attr("transform", d => `translate(${d.x}, ${d.y})`);
        });

        // --- 7. DRAG HANDLERS ---
        function dragstarted(event, d) {
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(event, d) {
            d.fx = event.x;
            d.fy = event.y;
        }

        function dragended(event, d) {
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }

        // --- 8. RESPONSIVE WINDOW RESIZING ---
        window.addEventListener('resize', () => {
            const w = container.clientWidth;
            const h = container.clientHeight;
            svg.attr("width", w).attr("height", h);
            simulation.force("center", d3.forceCenter(w / 2, h / 2)).alpha(0.3).restart();
        });
    </script>
</body>
</html>

Features included in this build:

  • Color-Coded Categories: Nodes are separated into core domains (Pitch, Harmony, Rhythm, Form) with a slick dark-mode UI and neon identifiers.

  • Dynamic Force Simulation: Nodes pull towards logical relatives and push away from unrelated concepts automatically to prevent overlapping text.

  • Interaction: Features fluid panning and zooming alongside click-and-drag mechanics to keep the map dynamic.

  • Adaptive Resizing: The application automatically repositions the center gravity if you adjust the dimensions of your browser window.


From <https://gemini.google.com/app/fbb80a89c657a74d>  Google Gemini (3.5 Flash)

Comments

Popular posts from this blog

#2892 Example of ChatGPT o1 Prompt

#3679 Google Gemini 2.5 Pro: Create Python Application to View and Query Neo4j KG