Skip to content

feat(highcharts): implement spectrogram-basic#6789

Merged
MarkusNeusinger merged 4 commits into
mainfrom
implementation/spectrogram-basic/highcharts
May 15, 2026
Merged

feat(highcharts): implement spectrogram-basic#6789
MarkusNeusinger merged 4 commits into
mainfrom
implementation/spectrogram-basic/highcharts

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Implementation: spectrogram-basic - python/highcharts

Implements the python/highcharts version of spectrogram-basic.

File: plots/spectrogram-basic/implementations/python/highcharts.py

Parent Issue: #2927


🤖 impl-generate workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 15, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Background is warm off-white (#FAF8F1) — correct light theme surface. Title "spectrogram-basic · highcharts · anyplot.ai" is visible at top center in dark text; subtitle "Linear chirp signal (10-200 Hz) with linear frequency axis" is visible below it. Y-axis label "Frequency (Hz)" and X-axis label "Time (seconds)" are both readable in dark text. The heatmap uses the viridis colormap (deep purple → blue → teal → green → bright yellow) and clearly shows the chirp signal as a diagonal yellow-green stripe rising from lower-left to upper-right — frequency sweeping from ~10 Hz at t=0 to ~200 Hz at t=2s. Colorbar on the right is labeled "Power (dB)". LAYOUT BUG: The heatmap occupies only the top ~37% of the canvas height. The bottom ~60% is empty background space populated with spurious y-axis labels (−3, −9, −15, −21, …, −99) that Highcharts auto-generates by extending the category axis below index 0 (no min constraint set). All actual text is readable against the light background. PASS for legibility, FAIL for canvas utilization.

Dark render (plot-dark.png): Background is warm near-black (#1A1A17) — correct dark theme surface. Title, subtitle, axis labels, tick labels, and colorbar text all appear in light colors (INK/INK_SOFT tokens correctly applied). No dark-on-dark failures detected. The heatmap data colors are identical to the light render — the same viridis gradient with the same bright diagonal chirp stripe. Only the background and chrome (text, colorbar frame) flipped between themes. The layout bug is identical: same spurious negative y-axis labels in the lower canvas area, same ~37% canvas utilization. All text readable against the dark background. PASS for legibility, FAIL for canvas utilization.

Both renders are confirmed viewed. Layout bug (y-axis not constrained to category range) is the primary defect in both renders.

Score: 82/100

Category Score Max
Visual Quality 28 30
Design Excellence 11 20
Spec Compliance 14 15
Data Quality 15 15
Code Quality 8 10
Library Mastery 6 10
Total 82 100

Visual Quality (28/30)

  • VQ-01: Text Legibility (8/8) — All sizes explicitly set: title 28px, axis labels 22px, ticks 18px. Readable in both themes.
  • VQ-02: No Overlap (6/6) — No overlapping elements in either render.
  • VQ-03: Element Visibility (6/6) — Heatmap cells and viridis colormap clearly visible; chirp stripe immediately recognizable.
  • VQ-04: Color Accessibility (2/2) — Viridis is perceptually uniform and colorblind-safe.
  • VQ-05: Layout & Canvas (2/4) — Heatmap fills only ~37% of canvas height. Bottom ~60% is wasted space due to Highcharts extending the category y-axis below index 0 (no min constraint set on y_axis).
  • VQ-06: Axis Labels & Title (2/2) — Time (seconds), Frequency (Hz), Power (dB) — all descriptive with units.
  • VQ-07: Palette Compliance (2/2) — Viridis stops correct for continuous data; backgrounds #FAF8F1 / #1A1A17; full theme-adaptive chrome.

Design Excellence (11/20)

  • DE-01: Aesthetic Sophistication (4/8) — Well-configured with subtitle and colorbar, but the layout bug undermines overall polish. Reads as a configured default rather than publication-ready.
  • DE-02: Visual Refinement (3/6) — Custom margins, grid opacity via GRID token, and colorbar sizing show refinement. However the large dead zone (bottom 60%) and heavy legend border reduce the overall visual quality.
  • DE-03: Data Storytelling (4/6) — The chirp diagonal stripe immediately conveys "frequency increases over time." Subtitle provides explicit context. Viridis makes the story visually compelling.

Spec Compliance (14/15)

  • SC-01: Plot Type (5/5) — Correct spectrogram as heatmap using Highcharts HeatmapSeries with real scipy.signal.spectrogram computation.
  • SC-02: Required Features (4/4) — Colorbar with dB units, time on x, frequency on y, dB scale, scipy spectrogram all present.
  • SC-03: Data Mapping (2/3) — Time/frequency mapping correct, but spurious negative y-axis labels create misleading impression of the frequency axis range.
  • SC-04: Title & Legend (3/3) — Title matches required format exactly. Colorbar labeled "Power (dB)".

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Chirp demonstrates time-frequency representation, frequency sweep, noise background, and dB scale.
  • DQ-02: Realistic Context (5/5) — Linear chirp (10-200 Hz, 1000 Hz sample rate, 2 s) is a real signal type used in radar, sonar, and audio analysis.
  • DQ-03: Appropriate Scale (4/4) — All values physically realistic.

Code Quality (8/10)

  • CQ-01: KISS Structure (2/3) — Class definition (QuietHTTPRequestHandler) and threading add structural complexity beyond a simple linear script.
  • CQ-02: Reproducibility (2/2) — np.random.seed(42) set.
  • CQ-03: Clean Imports (2/2) — All imports are used.
  • CQ-04: Code Elegance (1/2) — HTTP server + CDN approach is more complex than the recommended inline-script pattern from the library guide. Works, but over-engineered.
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html correctly.

Library Mastery (6/10)

  • LM-01: Idiomatic Usage (3/5) — Chart(container='container'), HeatmapSeries, color_axis used correctly. CDN+HTTP-server approach deviates from the recommended inline-script pattern in the library guide.
  • LM-02: Distinctive Features (3/5) — Uses Highcharts-specific HeatmapSeries, color_axis with viridis stops, interactive tooltip with pointFormat, and HTML export — all distinctive to Highcharts.

Score Caps Applied

  • None

Strengths

  • Full scipy.signal.spectrogram computation with real FFT, dB conversion, and downsampling for performance
  • Complete theme-adaptive chrome: PAGE_BG, INK, INK_SOFT, ELEVATED_BG, GRID tokens all correctly wired to ANYPLOT_THEME in both renders
  • Realistic chirp signal data immediately demonstrates the spectrogram concept; data storytelling is clear
  • Correct title format and colorbar labeling (Power (dB))
  • Viridis colormap applied via explicit stops — correct for continuous spectral data

Weaknesses

  • Y-axis category axis not constrained: Highcharts extends the category range below index 0, generating spurious negative labels (−3, −9, −15, … −99) in the lower canvas area. The heatmap then only occupies the top ~37% of the canvas. Fix by adding 'min': 0, 'max': len(frequencies_ds) - 1 to the y_axis options dict.
  • HTTP server + CDN approach over-engineered: The library guide recommends downloading Highcharts JS inline via urllib.request.urlopen. Replace the HTTP server + threading pattern with inline JS embedding.
  • Heavy legend border: The borderWidth: 1 on the legend box adds unnecessary visual weight; consider removing or reducing it.
  • CQ-01: Class definition (QuietHTTPRequestHandler) and threading add structural complexity; simplifying to inline scripts eliminates the need for these entirely.

Issues Found

  1. VQ-05 LOW (2/4): Y-axis category axis not constrained to valid range
    • The Highcharts category y-axis auto-extends below index 0, showing phantom negative labels and pushing the heatmap into only the top 37% of the canvas.
    • Fix: In the y_axis options dict, add 'min': 0, 'max': len(frequencies_ds) - 1 to clamp the axis to the category range. Also consider 'startOnTick': False, 'endOnTick': False.
  2. CQ-04 / LM-01: Non-idiomatic HTTP server approach
    • The library guide explicitly requires downloading Highcharts JS inline to avoid CDN issues with headless Chrome. The current HTTP server workaround is more complex and fragile.
    • Fix: Use urllib.request.urlopen to download highcharts.js and heatmap.js, embed both inline in the HTML <script> tags, remove the HTTP server and threading entirely.
  3. DE-01/DE-02: Generic aesthetic, heavy legend border
    • Fix: Remove borderWidth: 1 from legend, consider a lighter or borderless design.

AI Feedback for Next Attempt

Primary fix required: add 'min': 0, 'max': len(frequencies_ds) - 1 to the y_axis options to prevent Highcharts from auto-extending the category axis below index 0. This single fix will resolve the layout bug and bring canvas utilization to ~90%.

Secondary fix: replace the HTTP server + threading pattern with the library-guide-recommended inline JS approach (download highcharts.js and heatmap.js via urllib, embed inline in <script> tags). This simplifies the code significantly and eliminates the QuietHTTPRequestHandler class and threading.

Optional polish: remove the legend border (borderWidth: 0) for cleaner aesthetics; this would improve DE-02.

Verdict: REJECTED

@github-actions github-actions Bot added quality:82 Quality score 82/100 ai-rejected Quality not OK, triggers update labels May 15, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels May 15, 2026
@github-actions
Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

Status: Repair completed, re-triggering review...


🤖 impl-repair

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 15, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): The plot displays on a warm off-white background (#FAF8F1), correctly matching the light theme requirement. The heatmap occupies most of the canvas, showing a time-frequency spectrogram with the viridis color scale (dark purple for low power, bright yellow for high power). A clear diagonal yellow band traces the chirp sweep from ~10 Hz at t=0.06s to ~188 Hz at t=1.91s, which is the key spectral feature of the signal. The Y-axis shows Frequency (Hz) from 0 to 469 Hz with labeled ticks (0, 47, 94, 141, 188, 234, 281, 328, 375, 422, 469). The X-axis shows Time (seconds) with labels from 0.06 to 1.91. A vertical colorbar on the right labeled "Power (dB)" spans roughly -70 dB to -10 dB. The title "spectrogram-basic · highcharts · anyplot.ai" and subtitle "Linear chirp signal (10-200 Hz) with linear frequency axis" appear at top in dark text. All text (title, subtitle, axis labels, tick labels, colorbar labels) is clearly readable against the warm light background — dark-on-light, no legibility failures. Legibility verdict: PASS.

Dark render (plot-dark.png): The plot displays on a warm near-black background (#1A1A17), correctly matching the dark theme requirement. The spectrogram heatmap is identical to the light render — same viridis color scale, same diagonal chirp sweep pattern, same axis ranges. The data colors are identical between themes (viridis stops unchanged), confirming only chrome elements flipped. The title and subtitle appear at top in light-colored text (off-white) clearly readable against the dark background. Axis labels and tick labels are rendered in the INK_SOFT dark-theme token (#B8B7B0), clearly visible against the near-black surface. The colorbar retains the same viridis scale with legible dB labels. No dark-on-dark failures observed — all chrome text is appropriately light-colored for the dark theme. Legibility verdict: PASS.

Both paragraphs are required. A review that only describes one render is invalid.

Score: 85/100

Category Score Max
Visual Quality 28 30
Design Excellence 12 20
Spec Compliance 15 15
Data Quality 14 15
Code Quality 9 10
Library Mastery 7 10
Total 85 100

Visual Quality (28/30)

  • VQ-01: Text Legibility (7/8) — All font sizes explicitly set (title 28px, axis labels 22px, tick labels 18px); all text readable in both themes. Minor deduction as the colorbar legend title/labels are on the smaller end within the right margin column.
  • VQ-02: No Overlap (6/6) — No overlapping text; axis label stepping configured to avoid crowding.
  • VQ-03: Element Visibility (6/6) — Chirp sweep clearly visible as a bright diagonal band; heatmap cells well-sized at 80×50 grid; colorbar distinct.
  • VQ-04: Color Accessibility (2/2) — Viridis is perceptually uniform and fully CVD-safe; excellent contrast across the power range.
  • VQ-05: Layout & Canvas (3/4) — Plot fills approximately 70-75% of canvas; margins are balanced and accommodate colorbar well. Slight bottom whitespace below x-axis label prevents a perfect 4.
  • VQ-06: Axis Labels & Title (2/2) — X-axis: "Time (seconds)", Y-axis: "Frequency (Hz)", colorbar: "Power (dB)" — all descriptive with units.
  • VQ-07: Palette Compliance (2/2) — Viridis stops correctly applied to continuous data; light background #FAF8F1, dark background #1A1A17; all chrome tokens theme-adaptive in both renders.

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (4/8) — Well-configured professional appearance with proper theme tokens and viridis colormap, but falls in the "configured library default" tier. No custom typography, no distinctive visual flourish beyond correct implementation.
  • DE-02: Visual Refinement (4/6) — Custom margins (marginTop: 160, marginBottom: 250, marginLeft: 200, marginRight: 320), borderWidth: 0 removes cell borders for a cleaner look, subtle grid with GRID tokens. Noticeably above bare defaults.
  • DE-03: Data Storytelling (4/6) — Subtitle "Linear chirp signal (10-200 Hz) with linear frequency axis" immediately contextualizes the data; the viridis colormap creates a natural focal point as the yellow sweep stands out. Viewer immediately reads the chirp trajectory without annotation.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct spectrogram as time-frequency heatmap using HeatmapSeries.
  • SC-02: Required Features (4/4) — Color intensity encodes power; dB scale applied; time on x-axis, frequency on y-axis; colorbar with dB labels; chirp signal demonstrates non-stationary frequency content.
  • SC-03: Data Mapping (3/3) — Time (seconds) on X, Frequency (Hz) on Y, Power (dB) encoded in color — all correct.
  • SC-04: Title & Legend (3/3) — Title "spectrogram-basic · highcharts · anyplot.ai" matches required format exactly; colorbar labeled "Power (dB)".

Data Quality (14/15)

  • DQ-01: Feature Coverage (5/6) — Demonstrates chirp sweep (time-varying frequency), background noise, power dynamic range, and the full spectrogram structure. Minor deduction: the 80×50 downsampling loses some resolution that would better showcase fine frequency structure.
  • DQ-02: Realistic Context (5/5) — Chirp signal with gaussian noise is a standard, neutral signal processing test case. Scientifically realistic and non-controversial.
  • DQ-03: Appropriate Scale (4/4) — 1000 Hz sample rate, 2-second duration, 10-200 Hz sweep, dB scaling — all physically realistic and appropriate for the domain.

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3) — Sequential flow is clear, but the QuietHTTPRequestHandler class definition (inheriting SimpleHTTPRequestHandler) introduces a minor deviation from the pure imports→data→plot→save pattern.
  • CQ-02: Reproducibility (2/2) — np.random.seed(42) set before noise generation.
  • CQ-03: Clean Imports (2/2) — All imported modules are used.
  • CQ-04: Code Elegance (2/2) — The HTTP server approach is justified and non-trivial; the spectrogram pipeline is clean; try/finally ensures server cleanup.
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html correctly for an interactive library.

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5) — Correctly uses HeatmapSeries with [x, y, value] data format; color_axis for the colorbar; chart.to_js_literal(). Deduction: uses CDN scripts served via local HTTP server rather than the recommended inline embedding per highcharts.md guidelines — adds network dependency.
  • LM-02: Distinctive Features (3/5) — Leverages Highcharts-specific color_axis with custom viridis stops, symbolHeight/symbolWidth for the colorbar legend, and HeatmapSeries — features that are distinctive to Highcharts and not trivially portable to other libraries.

Score Caps Applied

  • None — DE-01=4 and DE-02=4, so the DE-01≤2 AND DE-02≤2 cap does not apply.

Strengths

  • Perfect spec compliance: correct spectrogram type with time-frequency heatmap, dB power scale, viridis colormap, and labeled colorbar
  • Excellent theme adaptation: both renders use correct background tokens (#FAF8F1/#1A1A17) with fully theme-adaptive chrome (INK, INK_SOFT tokens throughout); no legibility failures in either theme
  • Informative subtitle contextualizes the signal; the natural chirp sweep focal point (bright yellow diagonal band) provides immediate visual storytelling without annotations
  • Realistic signal processing pipeline (scipy chirp + STFT spectrogram) with appropriate dB scaling and noise for a scientifically credible example

Weaknesses

  • CDN + HTTP server approach deviates from the highcharts.md guideline to embed JS inline; introduces network dependency and extra complexity (threading, class definition)
  • Design sophistication is at the "well-configured defaults" tier (DE-01=4); lacks distinctive typographic or layout choices that would push to publication-ready quality
  • Downsampling to 80×50 grid creates blocky cell appearance; higher resolution (e.g., 120×70) would better represent the smooth frequency sweep and improve perceived quality

Issues Found

  1. LM-01 DEDUCTION: Uses `<script src="https://unpkg.com/...">" CDN links served via local HTTP server instead of inline JS embedding as required by highcharts.md. While clever, this adds network dependency risk.
    • Fix: Download highcharts.js and highcharts/modules/heatmap.js, embed inline as <script>{highcharts_js}</script> per the guide pattern.
  2. CQ-01 MINOR: QuietHTTPRequestHandler class definition deviates from KISS.
  3. DE-01 MODERATE: Aesthetic at "configured defaults" level.
    • Fix: Increase spectral resolution (larger nperseg or fewer downsampling steps); consider a brief annotation line marking the expected chirp trajectory to guide the viewer.

AI Feedback for Next Attempt

Switch to inline JS embedding (download highcharts.js + modules/heatmap.js, embed as inline <script> blocks) to eliminate the HTTP server, simplify the code to pure KISS, and remove network dependency. Increase spectrogram resolution by using max_time_bins=120, max_freq_bins=70 for crisper cells. To push DE-01 above 4, add a reference trajectory annotation (a dashed line from [0, f0] to [T, f1] in the expected-chirp color) and consider slightly larger title font (32px) for stronger visual hierarchy.

Verdict: APPROVED

@github-actions github-actions Bot added quality:85 Quality score 85/100 ai-approved Quality OK, ready for merge and removed quality:82 Quality score 82/100 labels May 15, 2026
@MarkusNeusinger MarkusNeusinger merged commit a242f8a into main May 15, 2026
3 checks passed
@MarkusNeusinger MarkusNeusinger deleted the implementation/spectrogram-basic/highcharts branch May 15, 2026 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-approved Quality OK, ready for merge ai-attempt-1 First repair attempt quality:85 Quality score 85/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant