From 03b2faed4243b116c061a0e409db9c80d3226292 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 03:42:53 +0000 Subject: [PATCH 1/5] feat(highcharts): implement piano-roll-midi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regen from quality 92. Addressed: - Canvas fixed to canonical 3200x1800 (was 4800x2700); added CDP setDeviceMetricsOverride + PIL dimension pin - Added ANYPLOT_THEME support with full theme-adaptive chrome (PAGE_BG, INK, INK_SOFT, GRID, ELEVATED_BG) - Output renamed to plot-{THEME}.png / plot-{THEME}.html - Title updated to correct format: piano-roll-midi · python · highcharts · anyplot.ai (was pyplots.ai, missing python token) - Font sizes updated to canonical 3200px values (66/56/44px) - Removed unused annotations.js download (CQ-03 fix) - Inlined vel_to_rgb helper function (CQ-01 KISS fix) - Increased bass/harmony pointWidth contrast (52 vs 30px) for colorblind safety; moved legend to bottom to free top margin Co-Authored-By: Claude Sonnet 4.6 --- .../implementations/python/highcharts.py | 282 +++++++++--------- 1 file changed, 145 insertions(+), 137 deletions(-) diff --git a/plots/piano-roll-midi/implementations/python/highcharts.py b/plots/piano-roll-midi/implementations/python/highcharts.py index 11bedc75ff..50c6e65f32 100644 --- a/plots/piano-roll-midi/implementations/python/highcharts.py +++ b/plots/piano-roll-midi/implementations/python/highcharts.py @@ -1,9 +1,10 @@ -""" pyplots.ai +"""anyplot.ai piano-roll-midi: MIDI Piano Roll Visualization -Library: highcharts unknown | Python 3.14.3 -Quality: 92/100 | Created: 2026-03-07 +Library: highcharts | Python 3.13 +Quality: 92/100 | Updated: 2026-06-03 """ +import os import tempfile import time import urllib.request @@ -12,19 +13,27 @@ import numpy as np from highcharts_core.chart import Chart from highcharts_core.options import HighchartsOptions +from PIL import Image from selenium import webdriver from selenium.webdriver.chrome.options import Options +# Theme tokens +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +GRID = "rgba(26,26,23,0.15)" if THEME == "light" else "rgba(240,239,232,0.15)" + np.random.seed(42) # MIDI helpers NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] BLACK_KEY_INDICES = {1, 3, 6, 8, 10} -# Data - A chord progression with melody (C-Am-F-G, 8 measures of 4/4) -# Each note: (start_beat, duration_beats, midi_pitch, velocity, role) -# role: "bass", "harmony", "melody" +# Data: C–Am–F–G chord progression, 8 measures of 4/4 +# (start_beat, duration_beats, midi_pitch, velocity, role) notes = [ # Measure 1-2: C major chord + melody (0.0, 4.0, 48, 90, "bass"), @@ -82,7 +91,7 @@ (21.0, 0.5, 67, 84, "melody"), (21.5, 0.5, 65, 80, "melody"), (22.0, 2.0, 64, 96, "melody"), - # Measure 7-8: G major chord + melody (building to end) + # Measure 7-8: G major chord + melody (24.0, 4.0, 55, 95, "bass"), (24.0, 2.0, 59, 76, "harmony"), (24.0, 2.0, 62, 76, "harmony"), @@ -102,172 +111,176 @@ (30.0, 2.0, 72, 125, "melody"), ] -# Determine pitch range - only include pitches that have notes (no empty rows) +# Pitch range — auto-fit to data used_pitches = sorted({n[2] for n in notes}) min_pitch = min(used_pitches) max_pitch = max(used_pitches) - -# Build category list for only the used range all_midi_range = list(range(min_pitch, max_pitch + 1)) categories = [] for midi in all_midi_range: octave = midi // 12 - 1 name = NOTE_NAMES[midi % 12] categories.append(f"{name}{octave}") - pitch_to_index = {midi: i for i, midi in enumerate(all_midi_range)} -# Velocity color mapping: teal (soft) -> amber (medium) -> crimson (loud) -# Using brighter amber midpoint for crisper transitions +# Velocity → color: teal (soft) → amber (medium) → crimson (loud) +# Inline stop triplets — no function vel_min, vel_max = 60, 127 -color_stops_rgb = [ - (20, 130, 150), # deep teal (soft/piano) - (255, 165, 0), # bright amber (medium/mezzo) - (185, 25, 40), # crimson (loud/forte) -] - +_s0, _s1, _s2 = (20, 130, 150), (255, 165, 0), (185, 25, 40) -def vel_to_rgb(t, stops=color_stops_rgb): - """Interpolate velocity 0..1 through three color stops.""" - if t < 0.5: - s = t / 0.5 - c0, c1 = stops[0], stops[1] - else: - s = (t - 0.5) / 0.5 - c0, c1 = stops[1], stops[2] - return tuple(int(c0[i] * (1 - s) + c1[i] * s) for i in range(3)) - - -# Pre-compute colorbar gradient segments (shared by Python→JS) -N_COLORBAR_SEGS = 80 -colorbar_colors = [] -for i in range(N_COLORBAR_SEGS): - t = 1 - i / (N_COLORBAR_SEGS - 1) - r, g, b = vel_to_rgb(t) - colorbar_colors.append(f"rgb({r},{g},{b})") - -# Role-specific styling +# Role config: distinct pointWidth + opacity + border for colorblind safety role_config = { - "melody": {"borderWidth": 2, "borderColor": "rgba(255, 255, 255, 0.7)", "pointWidth": 56, "opacity": 1.0}, - "harmony": {"borderWidth": 1, "borderColor": "rgba(0, 0, 0, 0.08)", "pointWidth": 48, "opacity": 0.72}, - "bass": {"borderWidth": 1, "borderColor": "rgba(0, 0, 0, 0.12)", "pointWidth": 64, "opacity": 0.85}, + "melody": {"borderWidth": 2, "borderColor": "rgba(255,255,255,0.85)", "pointWidth": 44, "opacity": 1.0}, + "harmony": {"borderWidth": 1, "borderColor": "rgba(0,0,0,0.06)", "pointWidth": 30, "opacity": 0.60}, + "bass": {"borderWidth": 2, "borderColor": "rgba(0,0,0,0.22)", "pointWidth": 52, "opacity": 0.90}, } -# Build xrange data points grouped by role +# Build series data — inline velocity interpolation series_data = {"melody": [], "harmony": [], "bass": []} for start, dur, pitch, vel, role in notes: t = float(np.clip((vel - vel_min) / (vel_max - vel_min), 0.0, 1.0)) - r, g, b = vel_to_rgb(t) + if t < 0.5: + s = t / 0.5 + rv = int(_s0[0] * (1 - s) + _s1[0] * s) + gv = int(_s0[1] * (1 - s) + _s1[1] * s) + bv = int(_s0[2] * (1 - s) + _s1[2] * s) + else: + s = (t - 0.5) / 0.5 + rv = int(_s1[0] * (1 - s) + _s2[0] * s) + gv = int(_s1[1] * (1 - s) + _s2[1] * s) + bv = int(_s1[2] * (1 - s) + _s2[2] * s) alpha = role_config[role]["opacity"] - color = f"rgba({r},{g},{b},{alpha})" series_data[role].append( { "x": start, "x2": start + dur, "y": pitch_to_index[pitch], - "color": color, + "color": f"rgba({rv},{gv},{bv},{alpha})", "custom": {"pitch": pitch, "velocity": vel, "noteName": categories[pitch_to_index[pitch]], "role": role}, } ) -# Plot bands for black keys (subtle darker background rows) +# Colorbar gradient (80 segments, top=loud/crimson, bottom=soft/teal) +colorbar_colors = [] +for i in range(80): + t = 1 - i / 79 + if t < 0.5: + s = t / 0.5 + rv = int(_s0[0] * (1 - s) + _s1[0] * s) + gv = int(_s0[1] * (1 - s) + _s1[1] * s) + bv = int(_s0[2] * (1 - s) + _s1[2] * s) + else: + s = (t - 0.5) / 0.5 + rv = int(_s1[0] * (1 - s) + _s2[0] * s) + gv = int(_s1[1] * (1 - s) + _s2[1] * s) + bv = int(_s1[2] * (1 - s) + _s2[2] * s) + colorbar_colors.append(f"rgb({rv},{gv},{bv})") + +# Black key row shading — theme-adaptive +band_color = "rgba(0,0,0,0.07)" if THEME == "light" else "rgba(255,255,255,0.07)" plot_bands = [] for midi in all_midi_range: if (midi % 12) in BLACK_KEY_INDICES: idx = pitch_to_index[midi] - plot_bands.append({"from": idx - 0.5, "to": idx + 0.5, "color": "rgba(0, 0, 0, 0.06)"}) + plot_bands.append({"from": idx - 0.5, "to": idx + 0.5, "color": band_color}) -# Beat grid lines for x-axis (stronger at measure boundaries) -total_beats = 32 +# Beat grid lines — stronger at measure boundaries beat_lines = [] -for beat in range(total_beats + 1): +for beat in range(33): is_measure = beat % 4 == 0 beat_lines.append( - {"value": beat, "color": "#999999" if is_measure else "#dddddd", "width": 2 if is_measure else 1, "zIndex": 3} + {"value": beat, "color": INK_SOFT if is_measure else GRID, "width": 2 if is_measure else 1, "zIndex": 3} ) # Chart chart = Chart(container="container") chart.options = HighchartsOptions() +title_text = "piano-roll-midi · python · highcharts · anyplot.ai" +n_title = len(title_text) +title_fs = round(66 * 67 / n_title) if n_title > 67 else 66 + chart.options.chart = { "type": "xrange", - "width": 4800, - "height": 2700, - "backgroundColor": "#fafafa", - "marginLeft": 200, - "marginTop": 180, - "marginBottom": 200, - "marginRight": 360, - "style": {"fontFamily": "'Segoe UI', 'Helvetica Neue', Arial, sans-serif"}, + "width": 3200, + "height": 1800, + "backgroundColor": PAGE_BG, + "marginLeft": 175, + "marginTop": 160, + "marginBottom": 155, + "marginRight": 245, + "style": {"fontFamily": "'Segoe UI', 'Helvetica Neue', Arial, sans-serif", "color": INK}, } chart.options.title = { - "text": "piano-roll-midi \u00b7 highcharts \u00b7 pyplots.ai", - "style": {"fontSize": "44px", "fontWeight": "600", "color": "#2c2c2c"}, - "y": 55, + "text": title_text, + "style": {"fontSize": f"{title_fs}px", "fontWeight": "600", "color": INK}, + "y": 78, } chart.options.subtitle = { - "text": "C \u2013 Am \u2013 F \u2013 G chord progression \u00b7 8 measures \u00b7 velocity-colored dynamics", - "style": {"fontSize": "26px", "color": "#666666", "fontWeight": "400"}, - "y": 100, + "text": "C – Am – F – G · 8 measures of 4/4 · velocity-colored dynamics", + "style": {"fontSize": "38px", "color": INK_SOFT, "fontWeight": "400"}, + "y": 126, } chart.options.x_axis = { - "title": {"text": "Beats (quarter notes)", "style": {"fontSize": "28px", "color": "#444444"}}, - "labels": {"style": {"fontSize": "22px", "color": "#555555"}, "step": 1}, + "title": {"text": "Beats (quarter notes)", "style": {"fontSize": "56px", "color": INK}}, + "labels": {"style": {"fontSize": "44px", "color": INK_SOFT}, "step": 1}, "min": 0, "max": 32, "tickInterval": 4, "gridLineWidth": 0, "plotLines": beat_lines, "lineWidth": 0, + "tickLength": 0, } chart.options.y_axis = { "type": "category", "categories": categories, - "title": {"text": "Pitch (note name)", "style": {"fontSize": "28px", "color": "#444444"}}, - "labels": {"style": {"fontSize": "22px", "color": "#555555"}}, + "title": {"text": "Pitch", "style": {"fontSize": "56px", "color": INK}}, + "labels": {"style": {"fontSize": "44px", "color": INK_SOFT}}, "gridLineWidth": 1, - "gridLineColor": "rgba(0, 0, 0, 0.04)", + "gridLineColor": GRID, "plotBands": plot_bands, "reversed": False, "lineWidth": 0, + "tickLength": 0, } chart.options.legend = { "enabled": True, - "align": "left", - "verticalAlign": "top", - "x": 220, - "y": 110, - "floating": True, - "itemStyle": {"fontSize": "22px", "fontWeight": "500", "color": "#444"}, - "itemDistance": 40, - "symbolWidth": 28, - "symbolHeight": 16, + "align": "center", + "verticalAlign": "bottom", + "itemStyle": {"fontSize": "44px", "fontWeight": "500", "color": INK_SOFT}, + "itemDistance": 56, + "symbolWidth": 40, + "symbolHeight": 22, "symbolRadius": 4, - "backgroundColor": "rgba(255,255,255,0.8)", - "borderWidth": 0, + "backgroundColor": ELEVATED_BG, + "borderColor": INK_SOFT, + "borderWidth": 1, + "borderRadius": 4, + "padding": 14, + "margin": 18, } chart.options.tooltip = { "headerFormat": "", - "pointFormat": '{point.custom.noteName} (MIDI {point.custom.pitch})
' - "Beat {point.x} \u2013 {point.x2}
" - "Velocity: {point.custom.velocity}
" - "Role: {point.custom.role}
", - "backgroundColor": "rgba(255,255,255,0.95)", - "borderColor": "#cccccc", + "pointFormat": ( + '{point.custom.noteName} (MIDI {point.custom.pitch})
' + "Beat {point.x} – {point.x2}
" + "Velocity: {point.custom.velocity}
" + "Role: {point.custom.role}
" + ), + "backgroundColor": ELEVATED_BG, + "borderColor": INK_SOFT, "borderRadius": 8, - "shadow": {"color": "rgba(0,0,0,0.15)", "offsetX": 2, "offsetY": 2, "width": 4}, + "style": {"color": INK}, } -# Three series for visual hierarchy: melody (prominent), bass (solid), harmony (subtle) series_configs = [("Melody", "melody", "#d94040"), ("Bass", "bass", "#2a7a8a"), ("Harmony", "harmony", "#c0a030")] - series_list = [] for label, role, legend_color in series_configs: cfg = role_config[role] @@ -278,25 +291,20 @@ def vel_to_rgb(t, stops=color_stops_rgb): "data": series_data[role], "color": legend_color, "pointWidth": cfg["pointWidth"], - "borderRadius": 5, + "borderRadius": 4, "borderWidth": cfg["borderWidth"], "borderColor": cfg["borderColor"], "dataLabels": {"enabled": False}, } ) chart.options.series = series_list - chart.options.credits = {"enabled": False} -# Download Highcharts JS modules +# Download Highcharts JS modules (with /tmp cache) cache_dir = Path("/tmp") cdn_urls = { "highcharts": ("https://cdn.jsdelivr.net/npm/highcharts@11.4.8/highcharts.js", cache_dir / "highcharts.js"), "xrange": ("https://cdn.jsdelivr.net/npm/highcharts@11.4.8/modules/xrange.js", cache_dir / "hc_xrange.js"), - "annotations": ( - "https://cdn.jsdelivr.net/npm/highcharts@11.4.8/modules/annotations.js", - cache_dir / "hc_annotations.js", - ), } js_scripts = {} for name, (url, cache_path) in cdn_urls.items(): @@ -309,32 +317,19 @@ def vel_to_rgb(t, stops=color_stops_rgb): js_scripts[name] = content highcharts_js = js_scripts["highcharts"] xrange_js = js_scripts["xrange"] -annotations_js = js_scripts["annotations"] -# Generate HTML with inline scripts and velocity colorbar via Highcharts renderer callback html_str = chart.to_js_literal() -# Chord labels positioned above the chart area -chord_labels = [ - {"text": "C", "beat": 2}, - {"text": "C", "beat": 6}, - {"text": "Am", "beat": 10}, - {"text": "Am", "beat": 14}, - {"text": "F", "beat": 18}, - {"text": "F", "beat": 22}, - {"text": "G", "beat": 26}, - {"text": "G", "beat": 30}, -] - -# JavaScript for chord labels and velocity colorbar +# Chord labels rendered above the plot area via Highcharts renderer +chord_labels = [("C", 2), ("C", 6), ("Am", 10), ("Am", 14), ("F", 18), ("F", 22), ("G", 26), ("G", 30)] chord_labels_js = "" -for cl in chord_labels: +for text, beat in chord_labels: chord_labels_js += f""" - r.text('{cl["text"]}', chart.xAxis[0].toPixels({cl["beat"]}), 155) - .attr({{'text-anchor':'middle'}}) - .css({{fontSize:'26px', color:'#888', fontWeight:'600', fontStyle:'italic'}}).add();""" + r.text('{text}', chart.xAxis[0].toPixels({beat}), 148) + .attr({{'text-anchor': 'middle'}}) + .css({{fontSize: '32px', color: '{INK_SOFT}', fontWeight: '600', fontStyle: 'italic'}}).add();""" -# Build JS array of pre-computed colors (eliminates duplicated interpolation in JS) +# Velocity colorbar via Highcharts renderer colors_js_array = "[" + ",".join(f"'{c}'" for c in colorbar_colors) + "]" colorbar_js = f""" @@ -345,18 +340,23 @@ def vel_to_rgb(t, stops=color_stops_rgb): if (!chart) return; clearInterval(checkChart); var r = chart.renderer; - var x = 4470, y = 220, w = 34, totalH = 2200; + var cx = 2958, cy = 162, cw = 26, totalH = 1410; var colors = {colors_js_array}; var nSegs = colors.length; var segH = totalH / nSegs; for (var i = 0; i < nSegs; i++) {{ - r.rect(x, y + i*segH, w, segH+1, 0).attr({{fill:colors[i], 'stroke-width':0}}).add(); + r.rect(cx, cy + i * segH, cw, segH + 1, 0).attr({{fill: colors[i], 'stroke-width': 0}}).add(); }} - r.rect(x, y, w, totalH, 6).attr({{fill:'none', stroke:'#bbb', 'stroke-width':1}}).add(); - r.text('Velocity', x + w/2, y - 15).attr({{'text-anchor':'middle'}}).css({{fontSize:'24px', color:'#444', fontWeight:'600'}}).add(); - r.text('127 (forte)', x + w + 14, y + 18).css({{fontSize:'20px', color:'#666'}}).add(); - r.text('93 (mezzo)', x + w + 14, y + totalH/2 + 6).css({{fontSize:'20px', color:'#666'}}).add(); - r.text('60 (piano)', x + w + 14, y + totalH - 4).css({{fontSize:'20px', color:'#666'}}).add(); + r.rect(cx, cy, cw, totalH, 5).attr({{fill: 'none', stroke: '{INK_SOFT}', 'stroke-width': 1}}).add(); + r.text('Velocity', cx + cw / 2, cy - 18) + .attr({{'text-anchor': 'middle'}}) + .css({{fontSize: '38px', color: '{INK}', fontWeight: '600'}}).add(); + r.text('forte', cx + cw + 14, cy + 20).css({{fontSize: '30px', color: '{INK_SOFT}'}}).add(); + r.text('127', cx + cw + 14, cy + 50).css({{fontSize: '28px', color: '{INK_SOFT}'}}).add(); + r.text('mezzo', cx + cw + 14, cy + totalH / 2 + 6).css({{fontSize: '30px', color: '{INK_SOFT}'}}).add(); + r.text('93', cx + cw + 14, cy + totalH / 2 + 36).css({{fontSize: '28px', color: '{INK_SOFT}'}}).add(); + r.text('piano', cx + cw + 14, cy + totalH - 14).css({{fontSize: '30px', color: '{INK_SOFT}'}}).add(); + r.text('60', cx + cw + 14, cy + totalH + 16).css({{fontSize: '28px', color: '{INK_SOFT}'}}).add(); {chord_labels_js} }}, 100); }})(); @@ -369,37 +369,45 @@ def vel_to_rgb(t, stops=color_stops_rgb): - - -
+ +
{colorbar_js} """ -# Save HTML -with open("plot.html", "w", encoding="utf-8") as f: +# Save HTML artifact +with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f: f.write(html_content) -# Screenshot with Selenium +# Screenshot via Selenium + CDP viewport override 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("--headless=new") 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=5000,3000") +chrome_options.add_argument("--hide-scrollbars") +chrome_options.add_argument("--window-size=3200,1800") driver = webdriver.Chrome(options=chrome_options) +driver.execute_cdp_cmd( + "Emulation.setDeviceMetricsOverride", {"width": 3200, "height": 1800, "deviceScaleFactor": 1, "mobile": False} +) driver.get(f"file://{temp_path}") time.sleep(5) - -container = driver.find_element("id", "container") -container.screenshot("plot.png") - +driver.save_screenshot(f"plot-{THEME}.png") driver.quit() + Path(temp_path).unlink() + +# Pin to exact canvas dimensions +_img = Image.open(f"plot-{THEME}.png").convert("RGB") +if _img.size != (3200, 1800): + _norm = Image.new("RGB", (3200, 1800), PAGE_BG) + _norm.paste(_img, ((3200 - _img.size[0]) // 2, (1800 - _img.size[1]) // 2)) + _norm.save(f"plot-{THEME}.png") From 2048e0dfede7cde50462bb168570da59f00d8e5c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 03:43:05 +0000 Subject: [PATCH 2/5] chore(highcharts): add metadata for piano-roll-midi --- .../metadata/python/highcharts.yaml | 244 ++---------------- 1 file changed, 15 insertions(+), 229 deletions(-) diff --git a/plots/piano-roll-midi/metadata/python/highcharts.yaml b/plots/piano-roll-midi/metadata/python/highcharts.yaml index f0c05f9932..05e8f3ad29 100644 --- a/plots/piano-roll-midi/metadata/python/highcharts.yaml +++ b/plots/piano-roll-midi/metadata/python/highcharts.yaml @@ -1,235 +1,21 @@ +# Per-library metadata for highcharts implementation of piano-roll-midi +# Auto-generated by impl-generate.yml + library: highcharts +language: python specification_id: piano-roll-midi created: '2026-03-07T19:49:41Z' -updated: '2026-03-07T20:22:03Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 22805912217 +updated: '2026-06-03T03:43:04Z' +generated_by: claude-sonnet +workflow_run: 26861938850 issue: 4565 -python_version: 3.14.3 +language_version: 3.13.13 library_version: unknown -preview_url: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/highcharts/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/highcharts/plot.html -quality_score: 92 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-dark.html +quality_score: null review: - strengths: - - Excellent musical data with realistic C-Am-F-G chord progression, three distinct - roles, and varied dynamics - - Professional velocity colorbar with teal-amber-crimson gradient and labeled intensity - levels - - Strong visual hierarchy through role-based opacity, border styling, and bar width - differentiation - - Chord labels above the chart provide musical context and narrative - - Black key row shading faithfully mirrors piano keyboard layout - weaknesses: - - Unused annotations module downloaded and embedded in HTML (minor code bloat) - - vel_to_rgb helper function breaks strict KISS structure (minor) - - Harmony and bass colors may blend at certain velocity ranges for colorblind viewers - image_description: 'The plot displays a MIDI piano roll visualization as horizontal - rectangles on a grid. The y-axis shows pitch names from C3 to C5, and the x-axis - shows beats from 0 to 32 (quarter notes). Notes are colored by velocity using - a teal (soft/piano) to amber (mezzo) to crimson (forte) gradient, with a vertical - colorbar on the right labeled "Velocity" with annotations for 127 (forte), 93 - (mezzo), and 60 (piano). Three series are distinguished: Melody (orange/red tones, - white borders, prominent), Bass (teal/green tones, wider bars), and Harmony (olive/amber - tones, more transparent). Chord labels (C, C, Am, Am, F, F, G, G) appear above - the plot area in italic gray text. Black key rows (C#, D#, F#, G#, A#) have subtle - gray shading to distinguish from white keys. Beat grid lines are visible with - stronger lines at measure boundaries (every 4 beats). The title reads "piano-roll-midi - · highcharts · pyplots.ai" with a subtitle "C – Am – F – G chord progression · - 8 measures · velocity-colored dynamics". The background is a light off-white. - A legend in the top-left shows Melody, Bass, and Harmony series.' - criteria_checklist: - visual_quality: - score: 29 - max: 30 - items: - - id: VQ-01 - name: Text Legibility - score: 8 - max: 8 - passed: true - comment: 'All font sizes explicitly set: title 44px, axis titles 28px, tick - labels 22px, legend 22px, subtitle 26px' - - id: VQ-02 - name: No Overlap - score: 6 - max: 6 - passed: true - comment: No text overlap anywhere. Chord labels, axis labels, legend, and - title all well-spaced - - id: VQ-03 - name: Element Visibility - score: 6 - max: 6 - passed: true - comment: Note rectangles well-sized with role-based widths (melody 56px, harmony - 48px, bass 64px) - - id: VQ-04 - name: Color Accessibility - score: 3 - max: 4 - passed: true - comment: Teal-amber-crimson avoids red-green conflicts but harmony and bass - may blend at certain velocities for colorblind viewers - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: Plot fills ~70% of canvas with balanced margins and well-placed colorbar - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: 'Descriptive labels: Beats (quarter notes) and Pitch (note name)' - design_excellence: - score: 17 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 7 - max: 8 - passed: true - comment: Custom velocity palette, role-based opacity/sizing, white melody - borders, chord labels, hand-drawn colorbar. Professional design. - - id: DE-02 - name: Visual Refinement - score: 5 - max: 6 - passed: true - comment: Off-white background, subtle grid, axis lines removed, plot bands - for black keys, rounded corners. Very polished. - - id: DE-03 - name: Data Storytelling - score: 5 - max: 6 - passed: true - comment: Three roles create visual hierarchy; chord labels add musical narrative; - velocity gradient tells dynamics story - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: Correct xrange chart type for horizontal note rectangles - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: 'All spec features present: note names, black/white key shading, - beat grid, velocity colors, auto-fit pitch range' - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: X=time in beats, Y=pitch. Correct mapping with full data range - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: Correct title format and clear legend labels for Melody, Bass, Harmony - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: Varying durations, velocities, three roles, chord progression, rhythmic - variation - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: Real C-Am-F-G chord progression with melody, harmony, and bass. Authentic - to DAW workflows - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: MIDI pitches 48-72 (C3-C5), velocities 60-125, 8 measures of 4/4 - time. All sensible - code_quality: - score: 8 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 2 - max: 3 - passed: true - comment: Mostly linear but contains vel_to_rgb function definition - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: np.random.seed(42) set and all data is hardcoded/deterministic - - id: CQ-03 - name: Clean Imports - score: 1 - max: 2 - passed: false - comment: Annotations Highcharts module downloaded and embedded but never used - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: Clean, well-organized code with pre-computed colorbar gradient and - role-based config - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: Saves as plot.png via Selenium screenshot. Current Highcharts API - library_mastery: - score: 8 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 4 - max: 5 - passed: true - comment: Good use of xrange series, HighchartsOptions, plotBands, plotLines, - tooltip formatting, to_js_literal() - - id: LM-02 - name: Distinctive Features - score: 4 - max: 5 - passed: true - comment: xrange chart type, renderer API for colorbar and chord labels, plotBands - for black keys, rich tooltips - verdict: APPROVED -impl_tags: - dependencies: - - selenium - techniques: - - hover-tooltips - - html-export - - custom-legend - - colorbar - patterns: - - data-generation - - iteration-over-groups - dataprep: [] - styling: - - custom-colormap - - alpha-blending - - grid-styling - - edge-highlighting + strengths: [] + weaknesses: [] From 3dfa6c14d2459409c024a1aee2cd56784de50b8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 03:51:29 +0000 Subject: [PATCH 3/5] chore(highcharts): update quality score 87 and review feedback for piano-roll-midi --- .../implementations/python/highcharts.py | 6 +- .../metadata/python/highcharts.yaml | 283 +++++++++++++++++- 2 files changed, 279 insertions(+), 10 deletions(-) diff --git a/plots/piano-roll-midi/implementations/python/highcharts.py b/plots/piano-roll-midi/implementations/python/highcharts.py index 50c6e65f32..9e597bf85a 100644 --- a/plots/piano-roll-midi/implementations/python/highcharts.py +++ b/plots/piano-roll-midi/implementations/python/highcharts.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai piano-roll-midi: MIDI Piano Roll Visualization -Library: highcharts | Python 3.13 -Quality: 92/100 | Updated: 2026-06-03 +Library: highcharts unknown | Python 3.13.13 +Quality: 87/100 | Updated: 2026-06-03 """ import os diff --git a/plots/piano-roll-midi/metadata/python/highcharts.yaml b/plots/piano-roll-midi/metadata/python/highcharts.yaml index 05e8f3ad29..d4da70f8ce 100644 --- a/plots/piano-roll-midi/metadata/python/highcharts.yaml +++ b/plots/piano-roll-midi/metadata/python/highcharts.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for highcharts implementation of piano-roll-midi -# Auto-generated by impl-generate.yml - library: highcharts language: python specification_id: piano-roll-midi created: '2026-03-07T19:49:41Z' -updated: '2026-06-03T03:43:04Z' +updated: '2026-06-03T03:51:29Z' generated_by: claude-sonnet workflow_run: 26861938850 issue: 4565 @@ -15,7 +12,279 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/piano-rol preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-dark.html -quality_score: null +quality_score: 87 review: - strengths: [] - weaknesses: [] + strengths: + - 'Full spec compliance: note names on Y-axis, alternating black-key row shading, + beat/measure gridlines, velocity colorbar, auto-fit pitch range — all implemented + correctly' + - Sophisticated velocity colormap with three-stop teal→amber→crimson gradient cleanly + encodes dynamics from piano to forte + - Musical role differentiation (Melody/Bass/Harmony) via distinct pointWidth, opacity, + and border styling adds visual hierarchy without requiring separate color channels + - Chord labels (C, Am, F, G) rendered above the plot area via Highcharts renderer + provide excellent musical context and storytelling + - 'Theme-adaptive chrome fully correct: PAGE_BG, INK, INK_SOFT, ELEVATED_BG, GRID + tokens applied consistently to all non-data elements in both themes' + - Canvas pinned to exact 3200×1800 via CDP override + PIL safety net — both renders + correct + - 'Realistic, educationally rich musical dataset: C–Am–F–G chord progression with + melody layered over harmony and bass across 8 measures of 4/4' + weaknesses: + - Chord labels at beats 10 and 14 ('Am') overlap vertically with the subtitle text + — subtitle is at y=126 (38px font, bottom ≈y=145) and chord labels are at y=148 + (32px font, top ≈y=132), creating a ~13px overlap zone; additionally, beats 10–14 + fall within the horizontal extent of the center-aligned subtitle, so the 'Am' + label visually collides with 'C – Am – F – G...' — fix by moving chord labels + to y=168 or increasing marginTop to 180 and setting chord labels at y=172 + - 'Continuous velocity colormap uses a custom teal→amber→crimson gradient instead + of the required imprint_seq or imprint_div — spec explicitly asks for ''blue for + piano/soft to red for forte/loud'' which maps naturally to reversed imprint_div + (#4467A3 at velocity=60, #FAF8F1/#1A1A17 at midpoint, #AE3030 at velocity=127); + swap the custom gradient for reversed imprint_div stops to satisfy both spec semantics + and palette policy' + - 'Categorical legend colors (#d94040 for Melody, #2a7a8a for Bass, #c0a030 for + Harmony) do not follow the Imprint palette — first series (Melody) must use #009E73; + assign Imprint palette positions 1–3 (#009E73, #C475FD, #4467A3) as the series + legend colors' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct anyplot light surface, not pure white. + Chrome: Title "piano-roll-midi · python · highcharts · anyplot.ai" in bold dark ink at top — fully readable. Subtitle "C – Am – F – G · 8 measures of 4/4 · velocity-colored dynamics" in INK_SOFT below title. X-axis label "Beats (quarter notes)" and Y-axis label "Pitch" in dark INK — both readable. Tick labels (note names C3–C5 on Y, beat numbers 0–32 on X) in INK_SOFT — readable. Chord labels (C, C, Am, Am, F, F, G, G) rendered in italic INK_SOFT above the plot area. + Data: Horizontal bars (xrange) color-mapped by velocity from teal (piano/60) through amber/orange (mezzo/93) to crimson/red (forte/127). Melody bars are widest (pointWidth=44) with white borders, bass bars are tallest (pointWidth=52) with dark borders and 0.9 opacity, harmony bars are narrowest (pointWidth=30) with 0.6 opacity. Alternating row shading for black-key rows is subtle and clearly visible. Vertical beat gridlines with stronger lines at 4-beat measure boundaries. Velocity colorbar at right edge with forte/127, mezzo/93, piano/60 labels. + Issue: Chord label "Am" at beat 10 overlaps vertically with the subtitle text — both occupy y≈132–148, causing "Am" to appear immediately before "C – Am – F – G" subtitle text. + Legibility verdict: PASS (all text readable; chord/subtitle overlap is visual noise, not a readability failure) + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct anyplot dark surface, not pure black. + Chrome: Title, axis labels, tick labels all in light F0EFE8/B8B7B0 tones — clearly readable against dark background. No dark-on-dark issues detected. Chord labels visible in INK_SOFT (B8B7B0). Colorbar labels visible. Legend items readable. + Data: Note colors are identical to the light render (teal/amber/crimson velocity gradient unchanged — data colors are theme-independent as required). Alternating black-key row shading uses rgba(255,255,255,0.07) — subtly visible against the dark background. Beat gridlines use GRID token (rgba(240,239,232,0.15)) with stronger measure boundaries in INK_SOFT. + Legibility verdict: PASS (all text readable against dark background; no dark-on-dark failures; brand green #009E73 not directly used as data color here since velocity mapping overrides, but INK_SOFT labels render correctly in B8B7B0) + criteria_checklist: + visual_quality: + score: 25 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: Font sizes at style-guide defaults (title 66px, axis labels 56px, + tick labels 44px). Readable in both themes. Minor deduction for chord labels + being very tight against subtitle text. + - id: VQ-02 + name: No Overlap + score: 4 + max: 6 + passed: false + comment: Chord labels 'Am' at beats 10 and 14 (y=148) vertically overlap with + subtitle text (y=126, 38px font, bottom ~y=145). Both text elements occupy + the same y band and same horizontal x range, creating a visual collision. + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: All note bars clearly visible. Velocity gradient from teal to crimson + clearly encodes dynamics. Alternating row shading for black keys is appropriately + subtle. Role differentiation via pointWidth clearly distinguishable. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Velocity colormap from teal through amber to crimson is CVD-safe + — not solely red-green, three perceptually distinct stops. + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: Canvas 3200x1800 correct. Good margins (marginLeft=175, marginBottom=155, + marginRight=245). Title proportion ~75% width — expected for this length. + Minor deduction for tight chord-label/subtitle spacing at top. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Title 'piano-roll-midi · python · highcharts · anyplot.ai' correct + format. X-axis 'Beats (quarter notes)' descriptive. Y-axis 'Pitch' with + note names (C3–C5) shown as categories. + - id: VQ-07 + name: Palette Compliance + score: 1 + max: 2 + passed: false + comment: 'Backgrounds #FAF8F1/#1A1A17 correct. Chrome tokens (INK, INK_SOFT, + ELEVATED_BG, GRID) correctly applied. BUT: continuous velocity colormap + is a custom teal→amber→crimson gradient instead of required imprint_seq + or imprint_div. Categorical legend colors (#d94040 Melody, #2a7a8a Bass, + #c0a030 Harmony) do not follow Imprint palette — first series must be #009E73.' + design_excellence: + score: 14 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: 'Well above baseline: custom velocity colormap with three-stop gradient, + role differentiation via pointWidth/opacity/border styling, black key row + shading, chord labels above plot via renderer, animated velocity colorbar + — all evidence of intentional design.' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Grid subtle (gridLineWidth=1 with GRID token), no visible spines + (lineWidth=0), alternating row shading is elegant. Good whitespace via generous + margins. Legend with elevated background and border is polished. + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: C–Am–F–G progression clearly visible. Velocity coloring immediately + shows dynamics (melody notes orange-to-red for high energy, harmony/bass + in cooler tones). Chord labels provide musical context. Clear focal point + on melody notes. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: xrange chart type = piano roll. Horizontal bars positioned by pitch + (Y) and time (X), length = duration. Correct. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Y-axis note names (C3–C5) ✓; alternating black-key row shading via + plotBands ✓; beat gridlines with stronger measure boundaries ✓; velocity + colorbar with piano/mezzo/forte labels ✓; auto-fit pitch range ✓. + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=start time (beats), X2=start+duration, Y=pitch (MIDI→note name), + color=velocity. All data fields correctly mapped. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title 'piano-roll-midi · python · highcharts · anyplot.ai' correct. + Legend shows Melody/Bass/Harmony roles. Subtitle provides additional musical + context. + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: 'Shows all piano roll dimensions: pitch (Y position), time onset + (X), duration (bar length), velocity (color), musical role (bar size). 70 + notes across 8 measures covering all aspects.' + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: C–Am–F–G is one of the most common chord progressions in popular + music. MIDI note numbers, timing, and velocity values are all musically + realistic. Neutral, educational content. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 32 beats (8 measures of 4/4), 70 notes, pitch range C3–C5, velocity + 60–127 — all appropriate for a short musical phrase demonstration. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: No functions or classes. Top-level procedural code throughout. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: np.random.seed(42) set. Data is hardcoded so deterministic regardless. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: All imports (os, tempfile, time, urllib.request, pathlib.Path, numpy, + highcharts_core, PIL.Image, selenium) are used. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Appropriate complexity for the chart type. Inline velocity interpolation + is slightly verbose but clear. No fake UI elements. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves both plot-{THEME}.png and plot-{THEME}.html. Current Highcharts + API (v11.4.8). + library_mastery: + score: 8 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Chart(container='container') correct. HighchartsOptions properly + configured. xrange type correctly applied. Options structure (chart/title/x_axis/y_axis/legend/tooltip/series) + idiomatic. + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: Uses xrange series type, plotBands for row shading, plotLines for + beat grid, Highcharts renderer for custom colorbar and chord labels, tooltip + with custom data properties. Distinctive Highcharts features well utilized. + verdict: APPROVED +impl_tags: + dependencies: + - selenium + - pillow + techniques: + - colorbar + - hover-tooltips + - html-export + patterns: + - data-generation + - iteration-over-groups + dataprep: + - normalization + styling: + - alpha-blending + - edge-highlighting + - grid-styling From 818a2f0f8cb99b5d66b19d8d642fbd42b8f3f1eb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 03:55:26 +0000 Subject: [PATCH 4/5] fix(highcharts): address review feedback for piano-roll-midi Attempt 1/3 - fixes based on AI review --- .../implementations/python/highcharts.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plots/piano-roll-midi/implementations/python/highcharts.py b/plots/piano-roll-midi/implementations/python/highcharts.py index 9e597bf85a..2a8351e4ff 100644 --- a/plots/piano-roll-midi/implementations/python/highcharts.py +++ b/plots/piano-roll-midi/implementations/python/highcharts.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai piano-roll-midi: MIDI Piano Roll Visualization Library: highcharts unknown | Python 3.13.13 Quality: 87/100 | Updated: 2026-06-03 @@ -123,10 +123,11 @@ categories.append(f"{name}{octave}") pitch_to_index = {midi: i for i, midi in enumerate(all_midi_range)} -# Velocity → color: teal (soft) → amber (medium) → crimson (loud) -# Inline stop triplets — no function +# Velocity → color: imprint_div reversed — blue (soft/piano) → theme midpoint → red (loud/forte) vel_min, vel_max = 60, 127 -_s0, _s1, _s2 = (20, 130, 150), (255, 165, 0), (185, 25, 40) +_s0 = (68, 103, 163) # #4467A3 blue at vel=60 +_s1 = (250, 248, 241) if THEME == "light" else (26, 26, 23) # #FAF8F1 / #1A1A17 midpoint +_s2 = (174, 48, 48) # #AE3030 red at vel=127 # Role config: distinct pointWidth + opacity + border for colorblind safety role_config = { @@ -280,7 +281,7 @@ "style": {"color": INK}, } -series_configs = [("Melody", "melody", "#d94040"), ("Bass", "bass", "#2a7a8a"), ("Harmony", "harmony", "#c0a030")] +series_configs = [("Melody", "melody", "#009E73"), ("Bass", "bass", "#C475FD"), ("Harmony", "harmony", "#4467A3")] series_list = [] for label, role, legend_color in series_configs: cfg = role_config[role] @@ -325,7 +326,7 @@ chord_labels_js = "" for text, beat in chord_labels: chord_labels_js += f""" - r.text('{text}', chart.xAxis[0].toPixels({beat}), 148) + r.text('{text}', chart.xAxis[0].toPixels({beat}), 168) .attr({{'text-anchor': 'middle'}}) .css({{fontSize: '32px', color: '{INK_SOFT}', fontWeight: '600', fontStyle: 'italic'}}).add();""" From 6a254ca39e3ca0d9f0abc6367d5401c20668e5f8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 04:03:19 +0000 Subject: [PATCH 5/5] chore(highcharts): update quality score 94 and review feedback for piano-roll-midi --- .../implementations/python/highcharts.py | 4 +- .../metadata/python/highcharts.yaml | 242 +++++++++--------- 2 files changed, 120 insertions(+), 126 deletions(-) diff --git a/plots/piano-roll-midi/implementations/python/highcharts.py b/plots/piano-roll-midi/implementations/python/highcharts.py index 2a8351e4ff..b85d13736b 100644 --- a/plots/piano-roll-midi/implementations/python/highcharts.py +++ b/plots/piano-roll-midi/implementations/python/highcharts.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai piano-roll-midi: MIDI Piano Roll Visualization Library: highcharts unknown | Python 3.13.13 -Quality: 87/100 | Updated: 2026-06-03 +Quality: 94/100 | Updated: 2026-06-03 """ import os diff --git a/plots/piano-roll-midi/metadata/python/highcharts.yaml b/plots/piano-roll-midi/metadata/python/highcharts.yaml index d4da70f8ce..9d321f9e3c 100644 --- a/plots/piano-roll-midi/metadata/python/highcharts.yaml +++ b/plots/piano-roll-midi/metadata/python/highcharts.yaml @@ -2,7 +2,7 @@ library: highcharts language: python specification_id: piano-roll-midi created: '2026-03-07T19:49:41Z' -updated: '2026-06-03T03:51:29Z' +updated: '2026-06-03T04:03:18Z' generated_by: claude-sonnet workflow_run: 26861938850 issue: 4565 @@ -12,146 +12,138 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/piano-rol preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/piano-roll-midi/python/highcharts/plot-dark.html -quality_score: 87 +quality_score: 94 review: strengths: - - 'Full spec compliance: note names on Y-axis, alternating black-key row shading, - beat/measure gridlines, velocity colorbar, auto-fit pitch range — all implemented - correctly' - - Sophisticated velocity colormap with three-stop teal→amber→crimson gradient cleanly - encodes dynamics from piano to forte - - Musical role differentiation (Melody/Bass/Harmony) via distinct pointWidth, opacity, - and border styling adds visual hierarchy without requiring separate color channels - - Chord labels (C, Am, F, G) rendered above the plot area via Highcharts renderer - provide excellent musical context and storytelling - - 'Theme-adaptive chrome fully correct: PAGE_BG, INK, INK_SOFT, ELEVATED_BG, GRID - tokens applied consistently to all non-data elements in both themes' - - Canvas pinned to exact 3200×1800 via CDP override + PIL safety net — both renders - correct - - 'Realistic, educationally rich musical dataset: C–Am–F–G chord progression with - melody layered over harmony and bass across 8 measures of 4/4' + - 'Perfect spec compliance — all piano roll requirements implemented: pitch-labeled + Y-axis, alternating black/white key shading, beat grid lines with stronger measure + boundaries, velocity-based diverging colormap' + - 'Sophisticated per-role visual differentiation: melody bars wider and fully opaque, + harmony bars narrower at 60% opacity, bass bars widest with dark border — provides + musical hierarchy without a separate encoding channel' + - 'Custom velocity colorbar built via Highcharts renderer API with correct imprint_div + colors (#4467A3 blue → #FAF8F1/#1A1A17 midpoint → #AE3030 red)' + - Chord annotations (C, Am, F, G) rendered above plot via Highcharts renderer, providing + musical storytelling context + - 'Correct canvas size (3200×1800), CDP override + PIL pin in place, both renders + properly themed (#FAF8F1 light / #1A1A17 dark)' + - 'Excellent code quality: flat KISS structure, seed set, all imports used, correct + output files saved' weaknesses: - - Chord labels at beats 10 and 14 ('Am') overlap vertically with the subtitle text - — subtitle is at y=126 (38px font, bottom ≈y=145) and chord labels are at y=148 - (32px font, top ≈y=132), creating a ~13px overlap zone; additionally, beats 10–14 - fall within the horizontal extent of the center-aligned subtitle, so the 'Am' - label visually collides with 'C – Am – F – G...' — fix by moving chord labels - to y=168 or increasing marginTop to 180 and setting chord labels at y=172 - - 'Continuous velocity colormap uses a custom teal→amber→crimson gradient instead - of the required imprint_seq or imprint_div — spec explicitly asks for ''blue for - piano/soft to red for forte/loud'' which maps naturally to reversed imprint_div - (#4467A3 at velocity=60, #FAF8F1/#1A1A17 at midpoint, #AE3030 at velocity=127); - swap the custom gradient for reversed imprint_div stops to satisfy both spec semantics - and palette policy' - - 'Categorical legend colors (#d94040 for Melody, #2a7a8a for Bass, #c0a030 for - Harmony) do not follow the Imprint palette — first series (Melody) must use #009E73; - assign Imprint palette positions 1–3 (#009E73, #C475FD, #4467A3) as the series - legend colors' + - Supplementary annotation text below the 44px floor — chord labels at 32px and + colorbar labels (forte/mezzo/piano at 30px, values at 28px) would be hard to read + when the plot is scaled down to mobile width (~400px); raise to at least 36–40px + - DE-03 storytelling could be stronger — no single visual focal point or climax + moment highlighted (e.g. the high-velocity G-measure peak at beat 29–30 with velocity + 120–125 could be annotated or emphasized) + - Harmony bars at opacity=0.6 on the dark background become noticeably dim; consider + bumping opacity to 0.72–0.78 for dark theme to maintain readability without losing + the hierarchy contrast image_description: |- Light render (plot-light.png): - Background: Warm off-white #FAF8F1 — correct anyplot light surface, not pure white. - Chrome: Title "piano-roll-midi · python · highcharts · anyplot.ai" in bold dark ink at top — fully readable. Subtitle "C – Am – F – G · 8 measures of 4/4 · velocity-colored dynamics" in INK_SOFT below title. X-axis label "Beats (quarter notes)" and Y-axis label "Pitch" in dark INK — both readable. Tick labels (note names C3–C5 on Y, beat numbers 0–32 on X) in INK_SOFT — readable. Chord labels (C, C, Am, Am, F, F, G, G) rendered in italic INK_SOFT above the plot area. - Data: Horizontal bars (xrange) color-mapped by velocity from teal (piano/60) through amber/orange (mezzo/93) to crimson/red (forte/127). Melody bars are widest (pointWidth=44) with white borders, bass bars are tallest (pointWidth=52) with dark borders and 0.9 opacity, harmony bars are narrowest (pointWidth=30) with 0.6 opacity. Alternating row shading for black-key rows is subtle and clearly visible. Vertical beat gridlines with stronger lines at 4-beat measure boundaries. Velocity colorbar at right edge with forte/127, mezzo/93, piano/60 labels. - Issue: Chord label "Am" at beat 10 overlaps vertically with the subtitle text — both occupy y≈132–148, causing "Am" to appear immediately before "C – Am – F – G" subtitle text. - Legibility verdict: PASS (all text readable; chord/subtitle overlap is visual noise, not a readability failure) + Background: Warm off-white #FAF8F1 — correct. Plot area has alternating slightly-darker horizontal bands marking black keys on the piano keyboard, visible against the cream background. + Chrome: Title "piano-roll-midi · python · highcharts · anyplot.ai" in dark ink at 66px, clearly readable. Subtitle "C – Am – F – G · 8 measures of 4/4 · velocity-colored dynamics" at 38px, readable. Y-axis label "Pitch" and X-axis label "Beats (quarter notes)" both at 56px, readable. Pitch tick labels (C3–C5 range) at 44px clearly readable. Beat tick labels (0, 4, 8 … 32) at 44px readable. Chord annotations (C, C, Am, Am, F, F, G, G) at 32px visible but small. Colorbar labels (forte/mezzo/piano at 30px, 127/93/60 at 28px) are readable at full resolution but small. Legend (Melody, Bass, Harmony) at 44px clearly readable at bottom. + Data: Note bars are velocity-colored using a diverging blue (#4467A3) → cream midpoint → red (#AE3030) scale. Low-velocity notes appear cool blue, high-velocity notes (G-measure climax at beats 28–31) are deep red. Melody bars are widest and fully opaque with white border. Bass bars are widest (52px) with dark border at 90% opacity. Harmony bars are narrowest at 60% opacity. Velocity colorbar on right correctly runs from red (forte/127) at top to blue (piano/60) at bottom. Beat grid lines visible with stronger lines at measures (beats 0, 4, 8, 12, 16, 20, 24, 28, 32). First series legend swatch is #009E73 green (Melody). + Legibility verdict: PASS — all primary text is readable; supplementary colorbar text (28–30px) and chord labels (32px) are slightly small but legible at full resolution. Dark render (plot-dark.png): - Background: Warm near-black #1A1A17 — correct anyplot dark surface, not pure black. - Chrome: Title, axis labels, tick labels all in light F0EFE8/B8B7B0 tones — clearly readable against dark background. No dark-on-dark issues detected. Chord labels visible in INK_SOFT (B8B7B0). Colorbar labels visible. Legend items readable. - Data: Note colors are identical to the light render (teal/amber/crimson velocity gradient unchanged — data colors are theme-independent as required). Alternating black-key row shading uses rgba(255,255,255,0.07) — subtly visible against the dark background. Beat gridlines use GRID token (rgba(240,239,232,0.15)) with stronger measure boundaries in INK_SOFT. - Legibility verdict: PASS (all text readable against dark background; no dark-on-dark failures; brand green #009E73 not directly used as data color here since velocity mapping overrides, but INK_SOFT labels render correctly in B8B7B0) + Background: Warm near-black #1A1A17 — correct. Same alternating black-key row shading at 7% white opacity, subtly visible against the dark background. + Chrome: Title, axis labels, tick labels all appear in light ink (#F0EFE8/INK) against the dark background. Readable. Chord annotations and colorbar labels similarly readable in their INK_SOFT color (#B8B7B0). Legend text readable with elevated-dark background (#242420). No dark-on-dark failures observed. + Data: Data colors are identical to light render — same velocity-based blue-to-red diverging scale. The note bars maintain the same hue and saturation regardless of theme (Imprint palette identity preserved). Harmony bars at 60% opacity appear slightly more muted on dark background than on light but remain distinguishable. Colorbar gradient identical. Melody white-border note bars pop cleanly against the dark background. + Legibility verdict: PASS — no dark-on-dark failures; all text readable. Harmony bars at opacity=0.6 are somewhat less prominent in the dark render but still visible. criteria_checklist: visual_quality: - score: 25 + score: 28 max: 30 items: - id: VQ-01 name: Text Legibility - score: 7 + score: 6 max: 8 passed: true - comment: Font sizes at style-guide defaults (title 66px, axis labels 56px, - tick labels 44px). Readable in both themes. Minor deduction for chord labels - being very tight against subtitle text. + comment: Primary labels (title 66px, axis titles 56px, tick labels 44px, legend + 44px) all correctly sized and readable in both themes. Chord annotations + at 32px and colorbar labels at 28-30px are below the 44px floor — readable + at full resolution but would struggle at mobile 400px scale. - id: VQ-02 name: No Overlap - score: 4 + score: 6 max: 6 - passed: false - comment: Chord labels 'Am' at beats 10 and 14 (y=148) vertically overlap with - subtitle text (y=126, 38px font, bottom ~y=145). Both text elements occupy - the same y band and same horizontal x range, creating a visual collision. + passed: true + comment: No overlapping text or data elements in either render. Per-role pointWidth + differentiation prevents bar overlap confusion. - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: All note bars clearly visible. Velocity gradient from teal to crimson - clearly encodes dynamics. Alternating row shading for black keys is appropriately - subtle. Role differentiation via pointWidth clearly distinguishable. + comment: All note bars clearly visible. Velocity colorbar gradient renders + correctly. Alternating key shading visible in both themes. Chord annotations + above plot area visible. - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Velocity colormap from teal through amber to crimson is CVD-safe - — not solely red-green, three perceptually distinct stops. + comment: Imprint diverging colormap (blue-to-red) is used for a continuous + velocity scale with labeled anchors (forte/mezzo/piano), providing non-color + cues. Role differentiation uses pointWidth and opacity as redundant encodings + beyond color. - id: VQ-05 name: Layout & Canvas - score: 3 + score: 4 max: 4 passed: true - comment: Canvas 3200x1800 correct. Good margins (marginLeft=175, marginBottom=155, - marginRight=245). Title proportion ~75% width — expected for this length. - Minor deduction for tight chord-label/subtitle spacing at top. + comment: 'Canvas 3200x1800 confirmed. Margins: left=175, top=160, bottom=155, + right=245 — sufficient for all labels. No clipping observed in either render. + Title at expected width. Canvas gate passed.' - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Title 'piano-roll-midi · python · highcharts · anyplot.ai' correct - format. X-axis 'Beats (quarter notes)' descriptive. Y-axis 'Pitch' with - note names (C3–C5) shown as categories. + comment: Title matches required format 'piano-roll-midi · python · highcharts + · anyplot.ai'. Y-axis 'Pitch' is concise and appropriate. X-axis 'Beats + (quarter notes)' includes units. - id: VQ-07 name: Palette Compliance - score: 1 + score: 2 max: 2 - passed: false - comment: 'Backgrounds #FAF8F1/#1A1A17 correct. Chrome tokens (INK, INK_SOFT, - ELEVATED_BG, GRID) correctly applied. BUT: continuous velocity colormap - is a custom teal→amber→crimson gradient instead of required imprint_seq - or imprint_div. Categorical legend colors (#d94040 Melody, #2a7a8a Bass, - #c0a030 Harmony) do not follow Imprint palette — first series must be #009E73.' + passed: true + comment: 'Velocity uses imprint_div (blue #4467A3 → PAGE_BG midpoint → red + #AE3030) — correct diverging cmap. Backgrounds #FAF8F1 (light) / #1A1A17 + (dark). First legend series (Melody) uses #009E73 brand green. All chrome + tokens theme-adaptive.' design_excellence: - score: 14 + score: 16 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 6 + score: 7 max: 8 passed: true - comment: 'Well above baseline: custom velocity colormap with three-stop gradient, - role differentiation via pointWidth/opacity/border styling, black key row - shading, chord labels above plot via renderer, animated velocity colorbar - — all evidence of intentional design.' + comment: 'Genuinely sophisticated: per-note velocity color interpolation, + per-role visual differentiation (pointWidth + opacity + border), piano keyboard + alternating row shading, custom velocity colorbar via Highcharts renderer, + chord annotations above plot, meaningful subtitle. Well above default.' - id: DE-02 name: Visual Refinement - score: 4 + score: 5 max: 6 passed: true - comment: Grid subtle (gridLineWidth=1 with GRID token), no visible spines - (lineWidth=0), alternating row shading is elegant. Good whitespace via generous - margins. Legend with elevated background and border is polished. + comment: Axes cleaned (lineWidth=0, tickLength=0). Credits disabled. Subtle + grid. Legend with rounded corners and proper padding. Black-key shading + subtle (7% opacity). Harmony opacity differentiation adds musical refinement. - id: DE-03 name: Data Storytelling score: 4 max: 6 passed: true - comment: C–Am–F–G progression clearly visible. Velocity coloring immediately - shows dynamics (melody notes orange-to-red for high energy, harmony/bass - in cooler tones). Chord labels provide musical context. Clear focal point - on melody notes. + comment: 'Chord progression annotations (C-Am-F-G) narrate the harmonic journey. + Velocity colorbar explains the dynamics arc. Subtitle contextualizes the + data. Missing: no explicit focal point or highlighted climax (the high-velocity + G-measure peak at beats 28-31 with velocity 115-125 could be emphasized).' spec_compliance: score: 15 max: 15 @@ -161,31 +153,30 @@ review: score: 5 max: 5 passed: true - comment: xrange chart type = piano roll. Horizontal bars positioned by pitch - (Y) and time (X), length = duration. Correct. + comment: Correct piano roll using xrange chart type. Horizontal rectangles + positioned by pitch (Y) and time (X) with bar length = duration. - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Y-axis note names (C3–C5) ✓; alternating black-key row shading via - plotBands ✓; beat gridlines with stronger measure boundaries ✓; velocity - colorbar with piano/mezzo/forte labels ✓; auto-fit pitch range ✓. + comment: 'All spec features present: note names on Y-axis, black/white key + row shading, beat division grid lines, stronger lines at measure boundaries, + velocity-to-color mapping.' - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: X=start time (beats), X2=start+duration, Y=pitch (MIDI→note name), - color=velocity. All data fields correctly mapped. + comment: Start on X, pitch on Y, duration as bar width (x to x2), velocity + as color. Pitch range auto-fit to data (C3-C5) with no excess. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title 'piano-roll-midi · python · highcharts · anyplot.ai' correct. - Legend shows Melody/Bass/Harmony roles. Subtitle provides additional musical - context. + comment: Title exactly 'piano-roll-midi · python · highcharts · anyplot.ai'. + Legend shows Melody/Bass/Harmony with correct first-series green swatch. data_quality: score: 15 max: 15 @@ -195,24 +186,24 @@ review: score: 6 max: 6 passed: true - comment: 'Shows all piano roll dimensions: pitch (Y position), time onset - (X), duration (bar length), velocity (color), musical role (bar size). 70 - notes across 8 measures covering all aspects.' + comment: 'Full coverage: multiple pitch octaves (C3-C5), varied note durations + (0.5 to 4.0 beats), velocity range 60-125, three musical roles (melody/bass/harmony), + 8 measures of 4/4, C-Am-F-G chord progression with 68 notes.' - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: C–Am–F–G is one of the most common chord progressions in popular - music. MIDI note numbers, timing, and velocity values are all musically - realistic. Neutral, educational content. + comment: C-Am-F-G is an iconic, neutral pop chord progression. Note velocities, + durations, and pitch ranges are authentic MIDI values. No controversial + or biased content. - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: 32 beats (8 measures of 4/4), 70 notes, pitch range C3–C5, velocity - 60–127 — all appropriate for a short musical phrase demonstration. + comment: 32 beats = 8 measures of 4/4. MIDI pitches 48-72 (C3-C5, appropriate + range around middle C). 68 notes total (within spec's 20-200 range). code_quality: score: 10 max: 10 @@ -222,69 +213,72 @@ review: score: 3 max: 3 passed: true - comment: No functions or classes. Top-level procedural code throughout. + comment: Flat, linear structure. No unnecessary functions or classes. - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: np.random.seed(42) set. Data is hardcoded so deterministic regardless. + comment: np.random.seed(42) set. Data is fully hardcoded so deterministic + regardless. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imports (os, tempfile, time, urllib.request, pathlib.Path, numpy, - highcharts_core, PIL.Image, selenium) are used. + comment: All imports (os, tempfile, time, urllib.request, pathlib, numpy, + highcharts_core, PIL, selenium) are used. - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Appropriate complexity for the chart type. Inline velocity interpolation - is slightly verbose but clear. No fake UI elements. + comment: Clean velocity interpolation logic. Inline JS for custom renderer + features is complex but unavoidable for this library's annotation pattern. + No fake interactivity. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves both plot-{THEME}.png and plot-{THEME}.html. Current Highcharts - API (v11.4.8). + comment: Saves plot-{THEME}.png and plot-{THEME}.html correctly. Uses highcharts-core + Python API idiomatically. library_mastery: - score: 8 + score: 10 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 4 + score: 5 max: 5 passed: true - comment: Chart(container='container') correct. HighchartsOptions properly - configured. xrange type correctly applied. Options structure (chart/title/x_axis/y_axis/legend/tooltip/series) - idiomatic. + comment: xrange chart type used correctly. chart.options object API used throughout. + Container='container' correctly set. Selenium CDP viewport override applied. + PIL pin for exact canvas dims. All Highcharts best practices followed. - id: LM-02 name: Distinctive Features - score: 4 + score: 5 max: 5 passed: true - comment: Uses xrange series type, plotBands for row shading, plotLines for - beat grid, Highcharts renderer for custom colorbar and chord labels, tooltip - with custom data properties. Distinctive Highcharts features well utilized. + comment: Highcharts renderer API used for custom colorbar and chord annotations + (not possible in static libs). plotBands for alternating key shading. plotLines + for beat/measure grid. Per-point color assignment in xrange data. JS cache + with /tmp for faster re-runs. verdict: APPROVED impl_tags: dependencies: - selenium - pillow techniques: - - colorbar - - hover-tooltips - html-export + - annotations + - colorbar + - manual-ticks patterns: - data-generation - iteration-over-groups dataprep: - normalization styling: + - custom-colormap - alpha-blending - - edge-highlighting - - grid-styling