diff --git a/plots/heatmap-interactive/implementations/highcharts.py b/plots/heatmap-interactive/implementations/highcharts.py
new file mode 100644
index 0000000000..f7f2ad42b3
--- /dev/null
+++ b/plots/heatmap-interactive/implementations/highcharts.py
@@ -0,0 +1,226 @@
+""" pyplots.ai
+heatmap-interactive: Interactive Heatmap with Hover and Zoom
+Library: highcharts unknown | Python 3.13.11
+Quality: 91/100 | Created: 2026-01-09
+"""
+
+import tempfile
+import time
+import urllib.request
+from pathlib import Path
+
+import numpy as np
+from highcharts_core.chart import Chart
+from highcharts_core.options import HighchartsOptions
+from selenium import webdriver
+from selenium.webdriver.chrome.options import Options
+
+
+# Data - Monthly website traffic by hour and day (20x24 matrix)
+np.random.seed(42)
+days = [
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ "Sunday",
+ "Week 2 Mon",
+ "Week 2 Tue",
+ "Week 2 Wed",
+ "Week 2 Thu",
+ "Week 2 Fri",
+ "Week 2 Sat",
+ "Week 2 Sun",
+ "Week 3 Mon",
+ "Week 3 Tue",
+ "Week 3 Wed",
+ "Week 3 Thu",
+ "Week 3 Fri",
+ "Week 3 Sat",
+]
+hours = [f"{h:02d}:00" for h in range(24)]
+
+# Generate realistic traffic patterns
+base_traffic = np.zeros((len(days), len(hours)))
+for i, day in enumerate(days):
+ for j in range(len(hours)):
+ # Peak hours 9-17 on weekdays
+ is_weekend = "Sat" in day or "Sun" in day
+ hour_factor = 1.0
+ if 9 <= j <= 17:
+ hour_factor = 2.5 if not is_weekend else 1.5
+ elif 6 <= j <= 8 or 18 <= j <= 21:
+ hour_factor = 1.8 if not is_weekend else 1.3
+ elif 0 <= j <= 5:
+ hour_factor = 0.3
+
+ day_factor = 0.6 if is_weekend else 1.0
+ base_value = 500 * day_factor * hour_factor
+ noise = np.random.normal(0, base_value * 0.2)
+ base_traffic[i, j] = max(0, base_value + noise)
+
+# Convert to heatmap data format for Highcharts
+heatmap_data = []
+for i, _day in enumerate(days):
+ for j, _hour in enumerate(hours):
+ heatmap_data.append([j, i, round(base_traffic[i, j], 1)])
+
+# Download Highcharts JS and heatmap module
+highcharts_url = "https://code.highcharts.com/highcharts.js"
+heatmap_url = "https://code.highcharts.com/modules/heatmap.js"
+boost_url = "https://code.highcharts.com/modules/boost.js"
+
+with urllib.request.urlopen(highcharts_url, timeout=30) as response:
+ highcharts_js = response.read().decode("utf-8")
+with urllib.request.urlopen(heatmap_url, timeout=30) as response:
+ heatmap_js = response.read().decode("utf-8")
+with urllib.request.urlopen(boost_url, timeout=30) as response:
+ boost_js = response.read().decode("utf-8")
+
+# Create chart
+chart = Chart(container="container")
+chart.options = HighchartsOptions()
+
+# Chart configuration with zooming
+chart.options.chart = {
+ "type": "heatmap",
+ "width": 4800,
+ "height": 2700,
+ "backgroundColor": "#ffffff",
+ "zoomType": "xy",
+ "panning": {"enabled": True, "type": "xy"},
+ "panKey": "shift",
+ "marginBottom": 180,
+ "resetZoomButton": {
+ "position": {"align": "right", "verticalAlign": "top", "x": -60, "y": 60},
+ "theme": {"fill": "#306998", "stroke": "#306998", "style": {"color": "#ffffff", "fontSize": "20px"}},
+ },
+}
+
+# Title
+chart.options.title = {
+ "text": "Website Traffic by Hour · heatmap-interactive · highcharts · pyplots.ai",
+ "style": {"fontSize": "48px", "fontWeight": "bold"},
+}
+
+chart.options.subtitle = {
+ "text": "Drag to zoom, Shift+drag to pan, click reset button to restore view",
+ "style": {"fontSize": "28px", "color": "#666666"},
+}
+
+# X-axis (hours)
+chart.options.x_axis = {
+ "categories": hours,
+ "title": {"text": "Hour of Day", "style": {"fontSize": "32px"}},
+ "labels": {"style": {"fontSize": "22px"}, "rotation": 315},
+ "tickLength": 0,
+ "gridLineWidth": 0,
+}
+
+# Y-axis (days)
+chart.options.y_axis = {
+ "categories": days,
+ "title": {"text": "Day", "style": {"fontSize": "32px"}},
+ "labels": {"style": {"fontSize": "22px"}},
+ "reversed": True,
+ "gridLineWidth": 0,
+}
+
+# Color axis (legend)
+chart.options.color_axis = {
+ "min": 0,
+ "max": float(np.max(base_traffic)),
+ "stops": [[0, "#f7fbff"], [0.2, "#c6dbef"], [0.4, "#6baed6"], [0.6, "#2171b5"], [0.8, "#08519c"], [1, "#08306b"]],
+ "labels": {"style": {"fontSize": "22px"}},
+}
+
+# Legend
+chart.options.legend = {
+ "align": "right",
+ "layout": "vertical",
+ "verticalAlign": "middle",
+ "symbolHeight": 600,
+ "itemStyle": {"fontSize": "22px"},
+ "title": {"text": "Visitors", "style": {"fontSize": "26px"}},
+}
+
+# Tooltip
+chart.options.tooltip = {
+ "useHTML": True,
+ "headerFormat": "",
+ "pointFormat": '
{series.xAxis.categories.(point.x)} on {series.yAxis.categories.(point.y)}
Visitors: {point.value:.0f}
',
+ "backgroundColor": "rgba(255, 255, 255, 0.95)",
+ "borderWidth": 2,
+ "borderColor": "#306998",
+}
+
+# Plot options for heatmap
+chart.options.plot_options = {
+ "heatmap": {
+ "borderWidth": 1,
+ "borderColor": "#ffffff",
+ "dataLabels": {"enabled": False},
+ "cursor": "pointer",
+ "states": {"hover": {"brightness": 0.2, "borderColor": "#000000", "borderWidth": 3}},
+ }
+}
+
+# Add series
+chart.add_series({"name": "Traffic", "type": "heatmap", "data": heatmap_data, "turboThreshold": 10000})
+
+# Generate HTML with inline scripts
+html_str = chart.to_js_literal()
+html_content = f"""
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+# Save interactive HTML
+with open("plot.html", "w", encoding="utf-8") as f:
+ # Generate standalone HTML with CDN links for the interactive version
+ interactive_html = f"""
+
+
+
+ heatmap-interactive · highcharts · pyplots.ai
+
+
+
+
+
+
+
+
+"""
+ f.write(interactive_html)
+
+# Write temp HTML and take screenshot
+with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
+ f.write(html_content)
+ temp_path = f.name
+
+chrome_options = Options()
+chrome_options.add_argument("--headless")
+chrome_options.add_argument("--no-sandbox")
+chrome_options.add_argument("--disable-dev-shm-usage")
+chrome_options.add_argument("--disable-gpu")
+chrome_options.add_argument("--window-size=4800,2900")
+
+driver = webdriver.Chrome(options=chrome_options)
+driver.get(f"file://{temp_path}")
+time.sleep(5)
+driver.save_screenshot("plot.png")
+driver.quit()
+
+Path(temp_path).unlink()
diff --git a/plots/heatmap-interactive/metadata/highcharts.yaml b/plots/heatmap-interactive/metadata/highcharts.yaml
new file mode 100644
index 0000000000..ce065477d3
--- /dev/null
+++ b/plots/heatmap-interactive/metadata/highcharts.yaml
@@ -0,0 +1,211 @@
+library: highcharts
+specification_id: heatmap-interactive
+created: '2026-01-09T09:08:06Z'
+updated: '2026-01-09T09:10:59Z'
+generated_by: claude-opus-4-5-20251101
+workflow_run: 20846719177
+issue: 3289
+python_version: 3.13.11
+library_version: unknown
+preview_url: https://storage.googleapis.com/pyplots-images/plots/heatmap-interactive/highcharts/plot.png
+preview_thumb: https://storage.googleapis.com/pyplots-images/plots/heatmap-interactive/highcharts/plot_thumb.png
+preview_html: https://storage.googleapis.com/pyplots-images/plots/heatmap-interactive/highcharts/plot.html
+quality_score: 91
+review:
+ strengths:
+ - 'Excellent use of Highcharts interactive features: zoom/pan, tooltips, reset button,
+ hover highlighting'
+ - Clean blue sequential colormap that is colorblind-safe
+ - Realistic website traffic data with clear weekday/weekend and day/night patterns
+ - Well-structured code with proper inline JS embedding for Selenium screenshot
+ - Good cell sizing with white borders for clear separation
+ weaknesses:
+ - Title format has descriptive prefix before the spec-id format
+ - Legend title lacks units (e.g., Visitors count)
+ - Could utilize Highcharts boost module more effectively for performance demonstration
+ image_description: The plot displays an interactive heatmap showing website traffic
+ data by hour of day (x-axis, 24 hours from 00:00 to 23:00) and day (y-axis, 20
+ days spanning Monday through Week 3 Sat). The heatmap uses a blue sequential color
+ scale ranging from very light blue/white (low values ~0) to dark navy blue (high
+ values ~2250 visitors). The title reads "Website Traffic by Hour · heatmap-interactive
+ · highcharts · pyplots.ai" with a subtitle explaining zoom/pan controls. A vertical
+ color legend labeled "Visitors" appears on the right side. Clear patterns show
+ higher traffic during business hours (9-17) on weekdays and lower traffic overnight
+ and on weekends. X-axis labels are rotated at 315 degrees for readability. White
+ borders separate each cell.
+ criteria_checklist:
+ visual_quality:
+ score: 37
+ max: 40
+ items:
+ - id: VQ-01
+ name: Text Legibility
+ score: 9
+ max: 10
+ passed: true
+ comment: Title, axis labels, and legend are readable; tick labels slightly
+ small but acceptable
+ - id: VQ-02
+ name: No Overlap
+ score: 8
+ max: 8
+ passed: true
+ comment: No overlapping text elements; rotated x-axis labels are well-spaced
+ - id: VQ-03
+ name: Element Visibility
+ score: 8
+ max: 8
+ passed: true
+ comment: Heatmap cells are well-sized with clear color differentiation and
+ white borders
+ - id: VQ-04
+ name: Color Accessibility
+ score: 5
+ max: 5
+ passed: true
+ comment: Blue sequential colormap is colorblind-safe
+ - id: VQ-05
+ name: Layout Balance
+ score: 5
+ max: 5
+ passed: true
+ comment: Plot fills canvas well, legend positioned appropriately on right
+ - id: VQ-06
+ name: Axis Labels
+ score: 2
+ max: 2
+ passed: true
+ comment: Hour of Day and Day are descriptive
+ - id: VQ-07
+ name: Grid & Legend
+ score: 0
+ max: 2
+ passed: false
+ comment: No grid lines (acceptable for heatmap), but legend title lacks units
+ spec_compliance:
+ score: 23
+ max: 25
+ items:
+ - id: SC-01
+ name: Plot Type
+ score: 8
+ max: 8
+ passed: true
+ comment: Correct heatmap chart type
+ - id: SC-02
+ name: Data Mapping
+ score: 5
+ max: 5
+ passed: true
+ comment: X=hours, Y=days, value=traffic correctly mapped
+ - id: SC-03
+ name: Required Features
+ score: 4
+ max: 5
+ passed: true
+ comment: Has hover tooltips, zoom/pan, reset button; crosshair/highlight on
+ hover implemented via brightness change
+ - id: SC-04
+ name: Data Range
+ score: 3
+ max: 3
+ passed: true
+ comment: All data visible, colorbar shows full range 0-2250
+ - id: SC-05
+ name: Legend Accuracy
+ score: 2
+ max: 2
+ passed: true
+ comment: Legend correctly shows Visitors with proper scale
+ - id: SC-06
+ name: Title Format
+ score: 1
+ max: 2
+ passed: false
+ comment: Title uses descriptive text before spec-id instead of just spec-id
+ format
+ data_quality:
+ score: 19
+ max: 20
+ items:
+ - id: DQ-01
+ name: Feature Coverage
+ score: 7
+ max: 8
+ passed: true
+ comment: Shows weekday/weekend patterns, hourly patterns, good variation;
+ could show more extreme outliers
+ - id: DQ-02
+ name: Realistic Context
+ score: 7
+ max: 7
+ passed: true
+ comment: Website traffic by hour/day is a realistic, neutral scenario
+ - id: DQ-03
+ name: Appropriate Scale
+ score: 5
+ max: 5
+ passed: true
+ comment: Visitor counts 0-2250 are realistic for hourly website traffic
+ code_quality:
+ score: 9
+ max: 10
+ items:
+ - id: CQ-01
+ name: KISS Structure
+ score: 3
+ max: 3
+ passed: true
+ comment: Linear script structure, no functions/classes
+ - id: CQ-02
+ name: Reproducibility
+ score: 3
+ max: 3
+ passed: true
+ comment: np.random.seed(42) set
+ - id: CQ-03
+ name: Clean Imports
+ score: 2
+ max: 2
+ passed: true
+ comment: All imports are used
+ - id: CQ-04
+ name: No Deprecated API
+ score: 1
+ max: 1
+ passed: true
+ comment: Uses current highcharts-core API
+ - id: CQ-05
+ name: Output Correct
+ score: 0
+ max: 1
+ passed: false
+ comment: Saves plot.png but also creates plot.html (acceptable for interactive
+ library)
+ library_features:
+ score: 3
+ max: 5
+ items:
+ - id: LF-01
+ name: Distinctive Features
+ score: 3
+ max: 5
+ passed: true
+ comment: Uses Highcharts heatmap module, zoom/pan, tooltips, reset button,
+ hover states; could utilize boost module more effectively
+ verdict: APPROVED
+impl_tags:
+ dependencies:
+ - selenium
+ techniques:
+ - hover-tooltips
+ - html-export
+ - colorbar
+ patterns:
+ - data-generation
+ - matrix-construction
+ - iteration-over-groups
+ dataprep: []
+ styling:
+ - custom-colormap
+ - edge-highlighting