Interactive SVG Heatmap Example

This example demonstrates how to create a dynamic and visually appealing heatmap using Scalable Vector Graphics (SVG). Heatmaps are excellent for visualizing the magnitude of a phenomenon as color in two dimensions. Hover over cells to see their specific values.

Value Intensity:
Low to High

Implementation Details

The heatmap is constructed using SVG elements (rectangles) where the fill color of each rectangle corresponds to a data value. A gradient color scale is used to represent the range of values. Interactive tooltips are implemented using JavaScript to display precise data on hover.


// Sample data (replace with your actual data)
const heatmapData = [
    [10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
    [15, 25, 35, 45, 55, 65, 75, 85, 95, 10],
    [20, 30, 40, 50, 60, 70, 80, 90, 10, 20],
    [25, 35, 45, 55, 65, 75, 85, 90, 20, 30],
    [30, 40, 50, 60, 70, 80, 90, 10, 30, 40],
    [35, 45, 55, 65, 75, 85, 90, 20, 40, 50],
    [40, 50, 60, 70, 80, 90, 10, 30, 50, 60],
    [45, 55, 65, 75, 85, 90, 20, 40, 60, 70],
    [50, 60, 70, 80, 90, 10, 30, 50, 70, 80],
    [55, 65, 75, 85, 90, 20, 40, 60, 80, 90]
];

const svg = document.getElementById('heatmapSvg');
const tooltip = document.getElementById('tooltip');
const svgWidth = svg.width.baseVal.value;
const svgHeight = svg.height.baseVal.value;
const numRows = heatmapData.length;
const numCols = heatmapData[0].length;

const cellWidth = svgWidth / numCols;
const cellHeight = svgHeight / numRows;

const minValue = Math.min(...heatmapData.flat());
const maxValue = Math.max(...heatmapData.flat());

// Define color stops for the gradient (you can customize this)
const colorStops = [
    { value: 0, color: '#f0fff0' },    // Honeydew (low)
    { value: 0.2, color: '#ffffb3' }, // Light Yellow
    { value: 0.4, color: '#ffe066' }, // Yellow
    { value: 0.6, color: '#ffcc00' }, // Gold
    { value: 0.8, color: '#ff9900' }, // Orange
    { value: 1, color: '#cc0000' }     // Red (high)
];

// Function to get color based on value
function getColor(value) {
    const normalizedValue = (value - minValue) / (maxValue - minValue);
    for (let i = 0; i < colorStops.length - 1; i++) {
        if (normalizedValue >= colorStops[i].value && normalizedValue <= colorStops[i + 1].value) {
            const progress = (normalizedValue - colorStops[i].value) / (colorStops[i + 1].value - colorStops[i].value);
            const startColor = hexToRgb(colorStops[i].color);
            const endColor = hexToRgb(colorStops[i + 1].color);
            const r = Math.round(startColor.r + (endColor.r - startColor.r) * progress);
            const g = Math.round(startColor.g + (endColor.g - startColor.g) * progress);
            const b = Math.round(startColor.b + (endColor.b - startColor.b) * progress);
            return `rgb(${r}, ${g}, ${b})`;
        }
    }
    return colorStops[colorStops.length - 1].color; // Default to max color if something goes wrong
}

// Helper function to convert hex color to RGB
function hexToRgb(hex) {
    const bigint = parseInt(hex.slice(1), 16);
    const r = (bigint >> 16) & 255;
    const g = (bigint >> 8) & 255;
    const b = bigint & 255;
    return { r, g, b };
}

// Create SVG elements
for (let i = 0; i < numRows; i++) {
    for (let j = 0; j < numCols; j++) {
        const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
        rect.setAttribute('x', j * cellWidth);
        rect.setAttribute('y', i * cellHeight);
        rect.setAttribute('width', cellWidth);
        rect.setAttribute('height', cellHeight);
        rect.setAttribute('fill', getColor(heatmapData[i][j]));
        rect.setAttribute('data-value', heatmapData[i][j]);
        rect.setAttribute('data-row', i);
        rect.setAttribute('data-col', j);

        rect.addEventListener('mouseover', (e) => {
            const value = e.target.getAttribute('data-value');
            const row = e.target.getAttribute('data-row');
            const col = e.target.getAttribute('data-col');
            tooltip.innerHTML = `Row: ${parseInt(row) + 1}, Col: ${parseInt(col) + 1}
Value: ${value}`; tooltip.style.opacity = '1'; tooltip.style.left = `${e.clientX + 15}px`; tooltip.style.top = `${e.clientY + 15}px`; }); rect.addEventListener('mousemove', (e) => { tooltip.style.left = `${e.clientX + 15}px`; tooltip.style.top = `${e.clientY + 15}px`; }); rect.addEventListener('mouseout', () => { tooltip.style.opacity = '0'; }); svg.appendChild(rect); } }