Skip to content

feat(chartjs): implement spectrogram-mel#8421

Merged
MarkusNeusinger merged 6 commits into
mainfrom
implementation/spectrogram-mel/chartjs
Jun 3, 2026
Merged

feat(chartjs): implement spectrogram-mel#8421
MarkusNeusinger merged 6 commits into
mainfrom
implementation/spectrogram-mel/chartjs

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Jun 3, 2026

Implementation: spectrogram-mel - javascript/chartjs

Implements the javascript/chartjs version of spectrogram-mel.

File: plots/spectrogram-mel/implementations/javascript/chartjs.js

Parent Issue: #4672


🤖 impl-generate workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 3, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): The plot renders on the correct warm off-white #FAF8F1 surface. The spectrogram fills the central chart area with the imprint_seq colormap: low-energy regions (-80 dB) fade toward the page background, medium-energy voiced segments render in teal-green (#009E73), and high-energy areas shift toward blue (#4467A3). Five voiced segments are clearly visible with horizontal formant band structures (F1, F2 resonances) and harmonic overtones, alternating with unvoiced segments that show broadband upper-frequency energy. The X-axis labels "0.0s"–"5.0s" (Time) and Y-axis labels "20"–"11k" Hz (mel-scaled) are in dark inkSoft, readable against the light background. Title "spectrogram-mel · javascript · chartjs · anyplot.ai" in dark ink at the top. Colorbar on the right with a pageBg→green→blue gradient, dB tick marks (0, −20, −40, −60, −80), and "Power (dB)" rotated label. All text clearly readable; no light-on-light issues.

Dark render (plot-dark.png): The plot renders on the correct near-black #1A1A17 surface. The spectrogram data colors are identical to the light render — teal-green (#009E73) voiced segments and blue (#4467A3) high-energy areas are unchanged; only the low-energy fill flips from off-white to near-black (pageBg→dark) which makes the quiet regions vanish into the background naturally. Title, axis labels, and tick labels render in light ink (#F0EFE8 / B8B7B0 tokens), clearly readable against the dark background. Colorbar dB labels and "Power (dB)" label in light text. No dark-on-dark failures observed — all chrome tokens correctly adapt. All text readable in both renders.

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

Score: 88/100

Category Score Max
Visual Quality 29 30
Design Excellence 13 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 7 10
Total 88 100

Visual Quality (29/30)

  • VQ-01: Text Legibility (7/8) — All font sizes explicitly set (title=22px CSS dynamic, axis=16px, ticks=14px, colorbar=max(11,H/36)). Readable in both themes. Minor deduction: title at 22px CSS fills ~45% canvas width, slightly below the 50–70% target.
  • VQ-02: No Overlap (6/6) — No overlapping text in either render; tick spacing managed with maxTicksLimit=7.
  • VQ-03: Element Visibility (6/6) — Spectrogram fills chart area with clear voiced/unvoiced contrast; colorbar prominent.
  • VQ-04: Color Accessibility (2/2) — imprint_seq provides strong luminance gradient; CVD-safe.
  • VQ-05: Layout & Canvas (4/4) — Canvas well-utilized; padding.right=110 correctly accommodates colorbar; balanced margins.
  • VQ-06: Axis Labels & Title (2/2) — "Time (s)", "Frequency (Hz, mel scale)", "Power (dB)" all with units.
  • VQ-07: Palette Compliance (2/2) — pageBg→t.seq[0]→t.seq[1] implements imprint_seq correctly. Backgrounds #FAF8F1/#1A1A17 correct. Data colors identical across both renders.

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) — Custom pixel rendering with bilinear interpolation is technically impressive for Chart.js; Imprint colormap correctly applied. Not publication-ready: chart boundary is a thin single-pixel inkSoft border; colorbar area lacks visual separation from the plot body; no elevated background behind the dB labels.
  • DE-02: Visual Refinement (4/6) — Good refinement: legend hidden (correct for continuous data), theme-adaptive grid and border. Deduction: grid lines show through the spectrogram and compete with the color signal — heavy enough to be noticeable.
  • DE-03: Data Storytelling (4/6) — Voiced/unvoiced alternating segments create a clear visual narrative; formant bands visible in voiced regions, broadband fricative energy in unvoiced regions. Immediately recognizable to an audio ML audience.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Mel-spectrogram with mel-scale warping, dB power encoding, time axis.
  • SC-02: Required Features (4/4) — Mel-scale frequency axis with Hz labels, dB colorbar, imprint_seq sequential colormap, synthesized audio data, n_mels=64 (within spec range 64–128).
  • SC-03: Data Mapping (3/3) — X=time 0–5s, Y=mel-scaled frequency 20Hz–~11kHz, color=power −80 to 0 dB.
  • SC-04: Title & Legend (3/3) — Title exactly spectrogram-mel · javascript · chartjs · anyplot.ai. No legend (correct for single continuous variable).

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Shows voiced segments (harmonics, F1/F2 formants), unvoiced fricative segments (upper-band broadband noise), full dB dynamic range, mel frequency compression.
  • DQ-02: Realistic Context (5/5) — Realistic speech analysis scenario; mel-spectrogram is the canonical input for ASR/speaker ID workflows. No controversial content.
  • DQ-03: Appropriate Scale (4/4) — Sample rate 22050 Hz, frequency 20Hz–Nyquist, duration 5s, dB range −80 to 0 — all industry-standard audio ML parameters.

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3) — 7 standalone helper functions (melToHz, hzToMelBand, makeLcg, generateSpec, hexToRgb, lerp, dbToColor) + plugin object exceed KISS ideal; complexity partly justified by chart type.
  • CQ-02: Reproducibility (2/2) — Fixed-seed LCG PRNG (seed=42); fully deterministic.
  • CQ-03: Clean Imports (2/2) — No imports; Chart.js is a global.
  • CQ-04: Code Elegance (2/2) — Clean and purposeful; no fake interactive elements; custom plugin is the correct Chart.js pattern for non-native chart types.
  • CQ-05: Output & API (1/1) — Correctly follows harness contract: canvas appended to #container, animation:false, responsive:true, maintainAspectRatio:false.

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (3/5) — Plugin API (afterDraw) used correctly. Using scatter type with empty data as a substrate is pragmatic but not the high-level Chart.js API.
  • LM-02: Distinctive Features (4/5) — Distinctive use of Chart.js plugin afterDraw + canvas ImageData API for pixel-level custom raster rendering; createLinearGradient for colorbar; full ANYPLOT_TOKENS integration for dual-theme adaptation.

Score Caps Applied

  • None — no caps triggered.

Strengths

  • Correct mel-spectrogram with proper mel-to-Hz mapping and realistic voiced/unvoiced speech synthesis (formants, harmonics, LCG PRNG seed=42)
  • imprint_seq colormap built correctly as pageBg→#009E73→#4467A3 with bilinear interpolation; data colors identical across light/dark renders
  • Complete spec compliance: dB colorbar with tick marks, time axis, mel-scaled frequency axis with Hz labels, synthesized data
  • Theme-adaptive chrome fully wired via ANYPLOT_TOKENS — no hardcoded colors

Weaknesses

  • DE-01: Chart boundary is a thin single-pixel inkSoft line — a slightly thicker border (1.5–2px) and an elevated background fill (t.elevatedBg) behind the colorbar area would lift visual weight and separate the data region from the margin
  • DE-02: Grid lines visible through the spectrogram compete with the color signal — reduce grid opacity further or disable the grid entirely (the spectrogram color encodes all the data; a grid adds noise, not information)
  • VQ-01/DE-01: Title at 22px CSS fills ~45% canvas width; increasing to 26px CSS would hit the 50–70% target and give the title more visual presence
  • CQ-01: Could inline lerp and hexToRgb into dbToColor to reduce standalone function count from 7 to 5

Issues Found

  1. DE-01 MEDIUM: Visual polish can be improved with minimal changes
    • Fix: Increase chart border lineWidth to 1.5–2, add ctx.fillStyle = t.elevatedBg; ctx.fillRect(cbX-4, top, cbW+60, cbH) behind colorbar area before drawing the gradient
  2. DE-02 MEDIUM: Grid lines compete with spectrogram color encoding
    • Fix: Set grid: { display: false } on both x and y scales — or reduce t.grid opacity to near-transparent; the spectrogram itself encodes the data
  3. VQ-01 MINOR: Title slightly small for canvas width
    • Fix: Change Math.round(22 * Math.min(1, 67 / titleText.length)) base from 22 to 26 so the title fills ~55% of canvas width

AI Feedback for Next Attempt

Primary improvements needed: (1) Remove or heavily suppress grid lines — they're visible through the spectrogram and add visual noise; set grid: { display: false } on both scales. (2) Add a subtle elevated background behind the colorbar area using t.elevatedBg to visually separate it from the main plot. (3) Increase title font size from 22px to 26px CSS to fill more of the canvas width. These are small changes that together lift DE-01 from 5→6 and DE-02 from 4→6, pushing the score above 90.

Verdict: REJECTED

@github-actions github-actions Bot added quality:88 Quality score: 88/100 ai-rejected Quality not OK, triggers update labels Jun 3, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels Jun 3, 2026
Attempt 1/3 - fixes based on AI review
@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented Jun 3, 2026

🔧 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 Jun 3, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): The plot renders on a warm off-white (#FAF8F1) background. The title "spectrogram-mel · javascript · chartjs · anyplot.ai" sits centered at top in dark ink — clearly readable. The Y-axis shows "Frequency (Hz, mel scale)" and the X-axis shows "Time (s)", both with dark label text and medium-dark tick labels (20, 1k, 3.5k, 7k, 11k Hz on Y; 0.0s–5.0s on X). The spectrogram fills the main chart area with vivid teal/green (#009E73) patches for voiced speech segments alternating with darker dimmer regions for unvoiced fricatives, with blue (#4467A3) for the highest-energy bins. A colorbar on the right spans the full chart height with an elevated off-white background, showing a gradient from blue (0 dB, top) through green to near-page-bg color (−80 dB, bottom), with tick labels (0, −20, −40, −60, −80) and a rotated "Power (dB)" label. All text is clearly readable against the light background — no light-on-light failures.

Dark render (plot-dark.png): The same visualization on a warm near-black (#1A1A17) background. Title, axis labels, and all tick labels are rendered in light text and are clearly readable against the dark surface — no dark-on-dark failures. The colorbar dB labels and "Power (dB)" rotated label are also light-colored and legible. The data colors (teal/green voiced regions, blue high-energy areas) are identical to the light render — only the chrome has flipped. The near-silence regions of the spectrogram now correctly fade to near-black (#1A1A17) rather than off-white, because the colormap base uses t.pageBg which is theme-adaptive. Both renders pass theme-readability checks.

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

Score: 91/100

Category Score Max
Visual Quality 29 30
Design Excellence 14 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 9 10
Total 91 100

Visual Quality (29/30)

  • VQ-01: Text Legibility (7/8) — All font sizes explicitly set (title=26, axis labels=16, ticks=14, colorbar font dynamically computed but set). Readable in both themes. Minor: colorbar font is Math.max(11, round(H/36)) which may render slightly small.
  • VQ-02: No Overlap (6/6) — No overlapping elements. Colorbar has an elevated background preventing content overlap.
  • VQ-03: Element Visibility (6/6) — Bilinear interpolation produces a smooth, high-contrast spectrogram. Voiced/unvoiced regions are visually distinct.
  • VQ-04: Color Accessibility (2/2) — imprint_seq (green → blue) is CVD-safe. Good luminance gradient.
  • VQ-05: Layout & Canvas (4/4) — Canvas gate passed. Landscape 3200×1800. Plot fills the frame well with right-side padding for the colorbar. No clipping.
  • VQ-06: Axis Labels & Title (2/2) — X: "Time (s)", Y: "Frequency (Hz, mel scale)", colorbar: "Power (dB)". All descriptive with units.
  • VQ-07: Palette Compliance (2/2) — imprint_seq correctly used for continuous data (t.seq[0]=#009E73 → t.seq[1]=#4467A3, base at t.pageBg). Backgrounds #FAF8F1 light / #1A1A17 dark. All chrome is theme-adaptive.

Design Excellence (14/20)

  • DE-01: Aesthetic Sophistication (6/8) — Strong design: custom Chart.js plugin with Canvas 2D pixel rendering, imprint_seq colormap, elevated colorbar background, theme-adaptive base color. Clearly above library defaults; thoughtful visual approach.
  • DE-02: Visual Refinement (4/6) — Grid disabled (clean). Elevated colorbar background adds separation. 2px border redrawn for visual structure. Top/bottom layout padding (10px) is tight for a 1800px-tall canvas.
  • DE-03: Data Storytelling (4/6) — Voiced/unvoiced alternation creates a strong visual rhythm and the energy distribution tells a clear story about speech audio. No annotations or callout labels to explicitly guide the viewer through the patterns.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct mel-spectrogram: frequency × time matrix, color = power in dB, mel scale on Y-axis.
  • SC-02: Required Features (4/4) — dB scale ✓, sequential colormap ✓, time X-axis ✓, mel-scale Y-axis with Hz labels ✓, colorbar labeled in dB ✓, synthesized speech-like audio data ✓.
  • SC-03: Data Mapping (3/3) — X = time (s), Y = mel band → Hz, color = power (dB). Correct.
  • SC-04: Title & Legend (3/3) — "spectrogram-mel · javascript · chartjs · anyplot.ai" ✓. No legend (single colormap dataset) — appropriate.

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Shows voiced segments (harmonics + F1/F2 formant resonances), unvoiced fricative (broadband upper-mel energy), and full -80 to 0 dB dynamic range.
  • DQ-02: Realistic Context (5/5) — Speech-like synthesis with fundamental harmonics, formant resonances, and voiced/unvoiced transitions. Realistic for an ASR / audio ML context.
  • DQ-03: Appropriate Scale (4/4) — 5s @ 22050 Hz SR, 64 mel bands, -80–0 dB range, formant positions in correct mel band ranges. Factually plausible.

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3) — Multiple helper functions (melToHz, hzToMelBand, makeLcg, generateSpec, dbToColor) exceed the flat-script ideal. Justified by visualization complexity. hzToMelBand is defined but never called (dead code).
  • CQ-02: Reproducibility (2/2) — Deterministic LCG PRNG with seed 42. Fully reproducible.
  • CQ-03: Clean Imports (2/2) — No imports; uses globals (window.ANYPLOT_TOKENS, Chart). Clean.
  • CQ-04: Code Elegance (2/2) — Well-organized: config → PRNG → data generation → colormap → plugin → mount → chart. No fake UI. Bilinear interpolation is appropriate.
  • CQ-05: Output & API (1/1) — animation: false ✓, responsive: true ✓, maintainAspectRatio: false ✓, no canvas.width/height ✓.

Library Mastery (9/10)

  • LM-01: Idiomatic Usage (5/5) — afterDraw plugin hook is the idiomatic Chart.js custom rendering pattern. Uses chartArea bounds, clip guards, offscreen canvas. Correct plugin contract.
  • LM-02: Distinctive Features (4/5) — Chart.js plugin API for custom Canvas 2D raster rendering within the chart lifecycle is a distinctive and advanced pattern. Bilinear interpolation + offscreen canvas blit is creative use of the platform. Core rendering is HTML5 Canvas rather than Chart.js native data types, which limits the score slightly.

Score Caps Applied

  • None — DE-01=6, DE-02=4, both > 2; no other caps triggered.

Strengths

  • Custom Chart.js plugin (afterDraw hook) renders pixel-perfect spectrogram via Canvas 2D with bilinear interpolation — visually smooth and sharp
  • Speech-like data synthesis with voiced/unvoiced alternation, harmonic series, and F1/F2 formant resonances gives the spectrogram a realistic and instructive structure
  • Deterministic LCG PRNG (seed 42) ensures full reproducibility
  • imprint_seq colormap correctly implemented with theme-adaptive base color: silence fades into the page surface (#FAF8F1 light / #1A1A17 dark)
  • Colorbar fully labeled ("Power (dB)", dB tick marks) with elevated background providing clean visual separation
  • Mel-to-Hz tick callback correctly converts mel band indices back to Hz for human-readable frequency labels

Weaknesses

  • hzToMelBand() is defined (lines 23–27) but never called — unused dead code should be removed
  • CQ-01: Several helper functions push past the KISS flat-script ideal; acceptable for this complexity but the unused function adds noise
  • DE-03: No annotation callouts labeling voiced vs. unvoiced segments — a subtle bracket or text label would improve storytelling
  • DE-02: Top/bottom layout padding (10px) is minimal for a 1800px-tall canvas; slightly more breathing room would improve visual balance

Issues Found

  1. CQ-01 MINOR: hzToMelBand defined but unused
    • Fix: Remove the function (3 lines) — it contributes nothing and represents dead code

AI Feedback for Next Attempt

Implementation is high quality. If repair is triggered, focus on: (1) remove the unused hzToMelBand function, (2) increase top/bottom layout padding from 10 to ~20–25 to give the chart more breathing room, (3) optionally add a subtle text annotation labeling one voiced and one unvoiced segment to improve storytelling (DE-03).

Verdict: APPROVED

@github-actions github-actions Bot added quality:91 Quality score 91/100 ai-approved Quality OK, ready for merge and removed quality:88 Quality score: 88/100 labels Jun 3, 2026
@MarkusNeusinger MarkusNeusinger merged commit 9f9cb1f into main Jun 3, 2026
@MarkusNeusinger MarkusNeusinger deleted the implementation/spectrogram-mel/chartjs branch June 3, 2026 18:43
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:91 Quality score 91/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant