From 1235f9f2110effdeb716b8978ae7a69c759c3ce3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 01:14:52 +0000 Subject: [PATCH 1/5] feat(altair): implement waveform-audio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regen from quality 92. Addressed: - Canvas: 4800×2700 → 3200×1800 (width=620, height=320, scale_factor=4.0, PIL pad) - Theme support: added ANYPLOT_THEME env var, PAGE_BG/INK/INK_SOFT/INK_MUTED tokens - Palette: waveform gradient updated from non-Imprint #306998 to Imprint brand green #009E73 - Font sizes: title 16px, subtitle 10px, axis title 12px, tick labels 10px (scale-based) - Bins: reduced 2000→600 to eliminate sub-pixel vertical striping artifacts - Removed unused np.random.seed(42) - Fixed save filenames: plot-{THEME}.png / plot-{THEME}.html --- .../implementations/python/altair.py | 117 ++++++++++++------ 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/plots/waveform-audio/implementations/python/altair.py b/plots/waveform-audio/implementations/python/altair.py index 76d102e4ab..0661a27b3d 100644 --- a/plots/waveform-audio/implementations/python/altair.py +++ b/plots/waveform-audio/implementations/python/altair.py @@ -1,22 +1,32 @@ -""" pyplots.ai +"""anyplot.ai waveform-audio: Audio Waveform Plot -Library: altair 6.0.0 | Python 3.14.3 -Quality: 92/100 | Created: 2026-03-07 +Library: altair """ +import os + import altair as alt import numpy as np import pandas as pd +from PIL import Image + +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" +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -# Data - synthetic audio waveform: sine tone with harmonics and amplitude envelope -np.random.seed(42) +# Imprint palette — semantic anchor for clipping regions +CLIP_COLOR = "#AE3030" # matte red: error/clipping role + +# Data - synthetic audio: 440 Hz tone with harmonics and amplitude envelope sample_rate = 22050 duration = 1.5 num_samples = int(sample_rate * duration) time = np.linspace(0, duration, num_samples) -# Primary tone (440 Hz) with harmonics fundamental = 440 signal = ( 0.6 * np.sin(2 * np.pi * fundamental * time) @@ -24,21 +34,20 @@ + 0.15 * np.sin(2 * np.pi * 3 * fundamental * time) ) -# Amplitude envelope: attack-sustain(with dip)-release with brief clipping section +# Attack-sustain-release envelope with amplitude dip and brief clipping boost envelope = np.ones_like(time) attack = int(0.05 * sample_rate) release = int(0.3 * sample_rate) envelope[:attack] = np.linspace(0, 1, attack) envelope[-release:] = np.linspace(1, 0, release) envelope[int(0.4 * sample_rate) : int(0.7 * sample_rate)] *= 0.5 -# Boost a short section to demonstrate clipping behavior envelope[int(0.15 * sample_rate) : int(0.25 * sample_rate)] *= 1.35 signal = signal * envelope signal = np.clip(signal, -1.0, 1.0) -# Vectorized min/max envelope binning for efficient rendering -num_bins = 2000 +# Min/max envelope binning — 600 bins avoids sub-pixel vertical striping artifacts +num_bins = 600 bin_size = num_samples // num_bins usable = num_bins * bin_size signal_trimmed = signal[:usable].reshape(num_bins, bin_size) @@ -53,25 +62,25 @@ ) df["clipped"] = (df["amp_max"] >= 0.99) | (df["amp_min"] <= -0.99) -# Shared x/y encodings +# Shared encodings x_enc = alt.X("time:Q", title="Time (seconds)", axis=alt.Axis(format=".2f", tickCount=8)) y_enc = alt.Y("amp_min:Q", title="Amplitude", scale=alt.Scale(domain=[-1.0, 1.0])) # Nearest-point selection for interactive crosshair nearest = alt.selection_point(nearest=True, on="pointerover", fields=["time"], empty=False) -# Main waveform fill with vertical gradient +# Main waveform: Imprint brand green (#009E73) vertical gradient waveform_gradient = ( alt.Chart(df) .mark_area( - interpolate="monotone", + interpolate="linear", color=alt.Gradient( gradient="linear", stops=[ - alt.GradientStop(color="rgba(48, 105, 152, 0.10)", offset=0), - alt.GradientStop(color="rgba(48, 105, 152, 0.60)", offset=0.45), - alt.GradientStop(color="rgba(48, 105, 152, 0.60)", offset=0.55), - alt.GradientStop(color="rgba(48, 105, 152, 0.10)", offset=1), + alt.GradientStop(color="rgba(0, 158, 115, 0.10)", offset=0), + alt.GradientStop(color="rgba(0, 158, 115, 0.60)", offset=0.45), + alt.GradientStop(color="rgba(0, 158, 115, 0.60)", offset=0.55), + alt.GradientStop(color="rgba(0, 158, 115, 0.10)", offset=1), ], x1=0, x2=0, @@ -83,34 +92,37 @@ .encode(x=x_enc, y=y_enc, y2="amp_max:Q") ) -# Clipped regions overlay (filtered layer with red tint) +# Clipped regions overlay — Imprint matte red (#AE3030) semantic anchor clipped_overlay = ( alt.Chart(df) - .mark_area(interpolate="monotone", color="rgba(180, 50, 50, 0.50)", line=False) + .mark_area(interpolate="linear", color="rgba(174, 48, 48, 0.50)", line=False) .encode(x="time:Q", y=y_enc, y2="amp_max:Q") .transform_filter(alt.datum.clipped == True) # noqa: E712 ) -# Zero baseline reference line +# Zero baseline reference line (theme-adaptive) zero_line = ( alt.Chart(pd.DataFrame({"y": [0]})) - .mark_rule(color="#306998", strokeWidth=1.5, opacity=0.35, strokeDash=[6, 4]) - .encode(y="y:Q") + .mark_rule(strokeWidth=1.5, opacity=0.35, strokeDash=[6, 4]) + .encode(y="y:Q", color=alt.value(INK_SOFT)) ) -# Clipping threshold lines at ±1.0 +# Clipping threshold lines at ±1.0 (semantic matte red) clip_lines = ( alt.Chart(pd.DataFrame({"y": [-1.0, 1.0]})) - .mark_rule(color="#b43232", strokeWidth=0.8, opacity=0.3, strokeDash=[3, 5]) - .encode(y="y:Q") + .mark_rule(strokeWidth=0.8, opacity=0.3, strokeDash=[3, 5]) + .encode(y="y:Q", color=alt.value(CLIP_COLOR)) ) -# Interactive vertical crosshair following the pointer +# Interactive crosshair following pointer crosshair_rule = ( - alt.Chart(df).mark_rule(color="#306998", strokeWidth=1, opacity=0.5).encode(x="time:Q").transform_filter(nearest) + alt.Chart(df) + .mark_rule(strokeWidth=1, opacity=0.5) + .encode(x="time:Q", color=alt.value(INK_SOFT)) + .transform_filter(nearest) ) -# Invisible selection trigger layer with tooltips +# Invisible selection trigger with tooltips selection_layer = ( alt.Chart(df) .mark_point(opacity=0) @@ -126,24 +138,49 @@ .add_params(nearest) ) -# Compose layers and style +# Compose layers and apply theme-adaptive configuration chart = ( alt.layer(waveform_gradient, clipped_overlay, zero_line, clip_lines, crosshair_rule, selection_layer) .properties( - width=1600, - height=900, + width=620, + height=320, + background=PAGE_BG, title=alt.Title( - "waveform-audio · altair · pyplots.ai", - fontSize=28, + "waveform-audio · altair · anyplot.ai", + fontSize=16, subtitle="440 Hz tone with harmonics · attack–sustain–release envelope · clipped region highlighted", - subtitleFontSize=16, - subtitleColor="#666666", + subtitleFontSize=10, ), ) - .configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.12, domainColor="#aaaaaa", tickColor="#aaaaaa") - .configure_view(strokeWidth=0) + .configure_view(fill=PAGE_BG, strokeWidth=0, continuousWidth=620, continuousHeight=320) + .configure_axis( + labelFontSize=10, + titleFontSize=12, + gridColor=INK, + gridOpacity=0.15, + domainColor=INK_SOFT, + tickColor=INK_SOFT, + labelColor=INK_SOFT, + titleColor=INK, + ) + .configure_title(color=INK, subtitleColor=INK_MUTED) ) -# Save -chart.save("plot.png", scale_factor=3.0) -chart.interactive().save("plot.html") +# Save PNG and pad to exact 3200×1800 (landscape target) +chart.save(f"plot-{THEME}.png", scale_factor=4.0) + +TW, TH = 3200, 1800 +_img = Image.open(f"plot-{THEME}.png").convert("RGB") +_w, _h = _img.size +if _w > TW or _h > TH: + raise SystemExit( + f"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. " + "Shrink chart .properties(width=, height=) values and re-render." + ) +if _w < TW or _h < TH: + _canvas = Image.new("RGB", (TW, TH), PAGE_BG) + _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2)) + _canvas.save(f"plot-{THEME}.png") + +# Save interactive HTML +chart.interactive().save(f"plot-{THEME}.html") From e42dbc6d18bfdd81a1df6ef59da6d3d6d06c7711 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 01:15:09 +0000 Subject: [PATCH 2/5] chore(altair): add metadata for waveform-audio --- .../metadata/python/altair.yaml | 236 ++---------------- 1 file changed, 16 insertions(+), 220 deletions(-) diff --git a/plots/waveform-audio/metadata/python/altair.yaml b/plots/waveform-audio/metadata/python/altair.yaml index 240fdffc2e..274d50a590 100644 --- a/plots/waveform-audio/metadata/python/altair.yaml +++ b/plots/waveform-audio/metadata/python/altair.yaml @@ -1,225 +1,21 @@ +# Per-library metadata for altair implementation of waveform-audio +# Auto-generated by impl-generate.yml + library: altair +language: python specification_id: waveform-audio created: '2026-03-07T14:58:17Z' -updated: '2026-03-07T15:11:56Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 22801238637 +updated: '2026-06-03T01:15:08Z' +generated_by: claude-sonnet +workflow_run: 26857420845 issue: 4563 -python_version: 3.14.3 -library_version: 6.0.0 -preview_url: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/altair/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/altair/plot.html -quality_score: 92 +language_version: 3.13.13 +library_version: 6.1.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-dark.html +quality_score: null review: - strengths: - - Excellent vertical gradient fill creating a professional DAW-like aesthetic - - Clipping region highlighted in red with transform_filter for effective visual - storytelling - - Min/max envelope binning (2000 bins from 33075 samples) for efficient rendering - - 'Strong use of Altair-specific features: gradients, selections, layer composition' - - Complete spec compliance with all required features present - - Clean well-structured code with vectorized operations - weaknesses: - - Minor vertical striping visible in the area chart from individual bin rendering - - np.random.seed(42) is set but no random functions are actually called - image_description: The plot displays an audio waveform as a filled area chart with - time (0–1.6 seconds) on the x-axis and normalized amplitude (-1.0 to +1.0) on - the y-axis. The main waveform body uses a steel-blue vertical gradient that is - more opaque near the zero baseline and fades toward the amplitude extremes, creating - a polished DAW-like appearance. A clipped region around 0.15–0.25 seconds is highlighted - with a semi-transparent red overlay where amplitude hits ±1.0. The envelope clearly - shows an attack phase (0–0.05s), a loud section with clipping, a sustained region, - a dip around 0.4–0.7s (half amplitude), then gradual release to silence. A dashed - blue zero-line and faint dashed red clip-threshold lines at ±1.0 are visible. - The title reads "waveform-audio · altair · pyplots.ai" with a descriptive subtitle. - Axis labels are large and readable. Grid lines are extremely subtle. The view - frame is removed for a clean look. - 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 28pt, axis titles 22pt, tick - labels 18pt, subtitle 16pt' - - id: VQ-02 - name: No Overlap - score: 6 - max: 6 - passed: true - comment: No overlapping text or elements - - id: VQ-03 - name: Element Visibility - score: 5 - max: 6 - passed: true - comment: Waveform envelope clearly visible with gradient; slight vertical - striping from bin rendering - - id: VQ-04 - name: Color Accessibility - score: 4 - max: 4 - passed: true - comment: Blue/red scheme distinguishable for colorblind users - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: 4800x2700 with balanced margins, good canvas utilization - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: Time (seconds) with units, Amplitude is appropriate for normalized - scale - design_excellence: - score: 14 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 6 - max: 8 - passed: true - comment: Strong design with vertical gradient, Python Blue palette, red clipping - overlay, descriptive subtitle - - id: DE-02 - name: Visual Refinement - score: 4 - max: 6 - passed: true - comment: View stroke removed, grid opacity 0.12, custom axis colors, dashed - reference lines - - id: DE-03 - name: Data Storytelling - score: 4 - max: 6 - passed: true - comment: Red clipping region as focal point, amplitude dip creates narrative, - subtitle narrates the story - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: Correct audio waveform as filled area chart symmetric around zero - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: 'All spec features present: filled area, semi-transparent gradient, - zero-line, envelope rendering, synthetic data' - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: X=time, Y=amplitude correctly assigned with explicit scale domain - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: Title format correct, no legend needed for single-series waveform - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: Shows dynamics, clipping, amplitude dip, harmonics, attack-sustain-release - envelope - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: 440 Hz concert A with harmonics at 22050 Hz sample rate - real audio - engineering scenario - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: Realistic sample rate, duration, and normalized amplitude values - code_quality: - score: 10 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: 'Clean linear flow: imports, data, envelope, binning, chart layers, - compose, save' - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: Seed set (data is deterministic via sine waves) - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: All three imports used - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: Well-organized with vectorized binning, appropriate complexity - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: Saves as plot.png and plot.html - library_mastery: - score: 9 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 5 - max: 5 - passed: true - comment: 'Expert use of declarative grammar: layered composition, encodings, - selections, transforms, configure' - - id: LM-02 - name: Distinctive Features - score: 4 - max: 5 - passed: true - comment: alt.Gradient, selection_point with nearest, transform_filter, layer - composition, interactive tooltips - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - layer-composition - - hover-tooltips - - html-export - patterns: - - data-generation - dataprep: - - binning - styling: - - gradient-fill - - grid-styling + strengths: [] + weaknesses: [] From 27116fd76460a2be86b97234fda4b1972aa6bd46 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 01:21:43 +0000 Subject: [PATCH 3/5] chore(altair): update quality score 85 and review feedback for waveform-audio --- .../implementations/python/altair.py | 5 +- .../metadata/python/altair.yaml | 244 +++++++++++++++++- 2 files changed, 240 insertions(+), 9 deletions(-) diff --git a/plots/waveform-audio/implementations/python/altair.py b/plots/waveform-audio/implementations/python/altair.py index 0661a27b3d..a9dbaf9972 100644 --- a/plots/waveform-audio/implementations/python/altair.py +++ b/plots/waveform-audio/implementations/python/altair.py @@ -1,6 +1,7 @@ -"""anyplot.ai +""" anyplot.ai waveform-audio: Audio Waveform Plot -Library: altair +Library: altair 6.1.0 | Python 3.13.13 +Quality: 85/100 | Updated: 2026-06-03 """ import os diff --git a/plots/waveform-audio/metadata/python/altair.yaml b/plots/waveform-audio/metadata/python/altair.yaml index 274d50a590..e49f16d15d 100644 --- a/plots/waveform-audio/metadata/python/altair.yaml +++ b/plots/waveform-audio/metadata/python/altair.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for altair implementation of waveform-audio -# Auto-generated by impl-generate.yml - library: altair language: python specification_id: waveform-audio created: '2026-03-07T14:58:17Z' -updated: '2026-06-03T01:15:08Z' +updated: '2026-06-03T01:21:42Z' generated_by: claude-sonnet workflow_run: 26857420845 issue: 4563 @@ -15,7 +12,240 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waveform- preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-dark.html -quality_score: null +quality_score: 85 review: - strengths: [] - weaknesses: [] + strengths: + - 'Correct use of Altair gradient fill API for waveform with #009E73 brand green + as primary series colour' + - Semantic red (#AE3030) used appropriately for clipping overlay — strongest visual + storytelling element + - 'Full spec coverage: zero line, clip threshold lines, min/max envelope binning, + ASR envelope, synthetic data' + - Interactive crosshair with tooltips leverages Altair distinctive interactivity; + HTML export saved alongside PNG + - KISS code structure with proper theme-adaptive chrome tokens throughout + - Correct PIL padding to exact 3200x1800 canvas with raise-on-overshoot guard + weaknesses: + - Missing 'python' language identifier in title — must be 'waveform-audio · python + · altair · anyplot.ai' (SC-04 violation) + - 'Dark render: gradient fill''s 10% minimum opacity causes near-invisible waveform + edges against #1A1A17, making the reduced-amplitude section (0.4-0.7 s) render + as jarring dark rectangular patches — raise gradient stop floor from 0.10 to at + least 0.25-0.30 opacity' + - 'Stepped waveform appearance: abrupt amplitude transitions between dipped region + (0.4-0.7 s) and surrounding sections produce sharp rectangular holes in both renders' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) — correct + Chrome: Title "waveform-audio · altair · anyplot.ai" bold and readable; subtitle in muted ink; Y-axis "Amplitude" and X-axis "Time (seconds)" legible; tick labels in dark ink + Data: Brand green (#009E73) gradient fill for waveform envelope (10% opacity at edges, 60% at centre); matte red (#AE3030) overlay for clipped region ~0.15-0.25s; dashed zero-line and faint red clip-threshold lines at ±1.0; rectangular background patches visible in reduced-amplitude 0.4-0.7s section + Legibility verdict: PASS + + Dark render (plot-dark.png): + Background: Near-black (#1A1A17) — correct + Chrome: Title, subtitle, axis labels, tick labels all in light ink — readable; no dark-on-dark text failures + Data: Colours identical to light render (green waveform, red clip overlay); in reduced-amplitude 0.4-0.7s section, gradient's 10% opacity floor becomes nearly invisible against dark background, creating jarring dark rectangular artefact patches that look like rendering errors + Legibility verdict: PASS (text only) — waveform rendering in dark theme has artefacts in low-amplitude section + criteria_checklist: + visual_quality: + score: 27 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All text readable in both themes; minor deduction for dark render + confusing waveform patches reducing overall readability impression + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No text or element collisions + - id: VQ-03 + name: Element Visibility + score: 4 + max: 6 + passed: false + comment: Waveform clearly visible on light; dark render has jarring near-black + rectangular patches in 0.4-0.7s section where 10% opacity gradient edges + vanish against dark background + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Red/green used for clip overlay vs main waveform; position encodes + clipping redundantly so CVD-safe + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: 3200x1800 confirmed, proportions good, no overflow + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Amplitude and Time (seconds) are descriptive with units + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: '#009E73 first series; #AE3030 semantic red for clipping; backgrounds + #FAF8F1 / #1A1A17 correct' + design_excellence: + score: 13 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Gradient fill is thoughtful; semantic red for clipping adds hierarchy; + dark-theme rendering artefacts undercut sophistication + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Grid at 15% opacity subtle; dashed zero-line and clip threshold lines + add structure; stepped waveform silhouette aesthetically rough + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Clipping highlighted effectively; subtitle names key features; ASR + envelope visible in shape; dark artefacts weaken story + spec_compliance: + score: 13 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: 'Correct waveform type: filled area symmetric around zero with min/max + envelope rendering' + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Semi-transparent fill, zero line, time x-axis, amplitude +-1.0, min/max + envelope, synthetic data all present + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=time in seconds, Y=normalized amplitude -1 to +1, full data visible + - id: SC-04 + name: Title & Legend + score: 1 + max: 3 + passed: false + comment: Title reads 'waveform-audio · altair · anyplot.ai' — missing required + 'python' language identifier; must be 'waveform-audio · python · altair + · anyplot.ai' + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 5 + max: 6 + passed: true + comment: Attack, sustain, release, clipping, harmonics all present; envelope + rendering is spec-correct + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: 440 Hz A-note with harmonics, ASR envelope, 22050 Hz sample rate, + normalized amplitude — convincingly realistic + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 1.5-second clip at 22050 Hz, 33075 samples, amplitude -1 to +1 — + all appropriate + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Flat script, no functions or classes + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Deterministic mathematical signal, no random seed needed + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: os, altair, numpy, pandas, PIL — all used + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean layer composition, transform_filter usage, proper noqa annotation + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot-{THEME}.png and plot-{THEME}.html using current Altair + API + library_mastery: + score: 8 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Layer composition, alt.Title with subtitle, configure_* pattern, + selection params — idiomatic throughout + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: Vega-Lite linear gradient fill, nearest-point selection crosshair, + interactive HTML export, transform_filter — distinctly Altair/Vega-Lite + features + verdict: APPROVED +impl_tags: + dependencies: + - pillow + techniques: + - hover-tooltips + - layer-composition + - html-export + patterns: + - data-generation + dataprep: + - binning + styling: + - gradient-fill + - alpha-blending From e5534e242ad358d56c1f0314a96f69e78a1fdda8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 01:27:16 +0000 Subject: [PATCH 4/5] fix(altair): address review feedback for waveform-audio Attempt 1/3 - fixes based on AI review --- .../implementations/python/altair.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/plots/waveform-audio/implementations/python/altair.py b/plots/waveform-audio/implementations/python/altair.py index a9dbaf9972..3dd24a22cf 100644 --- a/plots/waveform-audio/implementations/python/altair.py +++ b/plots/waveform-audio/implementations/python/altair.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai waveform-audio: Audio Waveform Plot Library: altair 6.1.0 | Python 3.13.13 Quality: 85/100 | Updated: 2026-06-03 @@ -41,7 +41,18 @@ release = int(0.3 * sample_rate) envelope[:attack] = np.linspace(0, 1, attack) envelope[-release:] = np.linspace(1, 0, release) -envelope[int(0.4 * sample_rate) : int(0.7 * sample_rate)] *= 0.5 + +# Smooth amplitude dip (0.4-0.7 s) via cosine taper — avoids abrupt rectangular step +dip_start = int(0.4 * sample_rate) +dip_end = int(0.7 * sample_rate) +transition_len = int(0.035 * sample_rate) +t_fade = np.linspace(0, 1, transition_len) +dip_mult = np.ones_like(time) +dip_mult[dip_start : dip_start + transition_len] = 1.0 - 0.25 * (1 - np.cos(np.pi * t_fade)) +dip_mult[dip_start + transition_len : dip_end - transition_len] = 0.5 +dip_mult[dip_end - transition_len : dip_end] = 0.5 + 0.25 * (1 - np.cos(np.pi * t_fade)) +envelope *= dip_mult + envelope[int(0.15 * sample_rate) : int(0.25 * sample_rate)] *= 1.35 signal = signal * envelope @@ -78,10 +89,10 @@ color=alt.Gradient( gradient="linear", stops=[ - alt.GradientStop(color="rgba(0, 158, 115, 0.10)", offset=0), + alt.GradientStop(color="rgba(0, 158, 115, 0.28)", offset=0), alt.GradientStop(color="rgba(0, 158, 115, 0.60)", offset=0.45), alt.GradientStop(color="rgba(0, 158, 115, 0.60)", offset=0.55), - alt.GradientStop(color="rgba(0, 158, 115, 0.10)", offset=1), + alt.GradientStop(color="rgba(0, 158, 115, 0.28)", offset=1), ], x1=0, x2=0, @@ -147,7 +158,7 @@ height=320, background=PAGE_BG, title=alt.Title( - "waveform-audio · altair · anyplot.ai", + "waveform-audio · python · altair · anyplot.ai", fontSize=16, subtitle="440 Hz tone with harmonics · attack–sustain–release envelope · clipped region highlighted", subtitleFontSize=10, From 41737ddd4a3d92047d7762307e809907b4f799fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 01:34:45 +0000 Subject: [PATCH 5/5] chore(altair): update quality score 92 and review feedback for waveform-audio --- .../implementations/python/altair.py | 4 +- .../metadata/python/altair.yaml | 178 ++++++++++-------- 2 files changed, 97 insertions(+), 85 deletions(-) diff --git a/plots/waveform-audio/implementations/python/altair.py b/plots/waveform-audio/implementations/python/altair.py index 3dd24a22cf..1408903db9 100644 --- a/plots/waveform-audio/implementations/python/altair.py +++ b/plots/waveform-audio/implementations/python/altair.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai waveform-audio: Audio Waveform Plot Library: altair 6.1.0 | Python 3.13.13 -Quality: 85/100 | Updated: 2026-06-03 +Quality: 92/100 | Updated: 2026-06-03 """ import os diff --git a/plots/waveform-audio/metadata/python/altair.yaml b/plots/waveform-audio/metadata/python/altair.yaml index e49f16d15d..9b198acf88 100644 --- a/plots/waveform-audio/metadata/python/altair.yaml +++ b/plots/waveform-audio/metadata/python/altair.yaml @@ -2,7 +2,7 @@ library: altair language: python specification_id: waveform-audio created: '2026-03-07T14:58:17Z' -updated: '2026-06-03T01:21:42Z' +updated: '2026-06-03T01:34:44Z' generated_by: claude-sonnet workflow_run: 26857420845 issue: 4563 @@ -12,92 +12,99 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waveform- preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/python/altair/plot-dark.html -quality_score: 85 +quality_score: 92 review: strengths: - - 'Correct use of Altair gradient fill API for waveform with #009E73 brand green - as primary series colour' - - Semantic red (#AE3030) used appropriately for clipping overlay — strongest visual - storytelling element - - 'Full spec coverage: zero line, clip threshold lines, min/max envelope binning, - ASR envelope, synthetic data' - - Interactive crosshair with tooltips leverages Altair distinctive interactivity; - HTML export saved alongside PNG - - KISS code structure with proper theme-adaptive chrome tokens throughout - - Correct PIL padding to exact 3200x1800 canvas with raise-on-overshoot guard + - Vertical gradient fill on mark_area is a sophisticated aesthetic choice — the + opacity taper from center (0.60) to edges (0.28) gives the waveform visual depth + - Semantic use of Imprint matte red (#AE3030) for clipping overlay is intentional + and correctly represents the audio engineering concept + - Min/max envelope binning (600 bins) is the correct approach for this dense signal + and avoids sub-pixel aliasing artifacts + - Theme-adaptive chrome is fully wired (PAGE_BG, INK, INK_SOFT, INK_MUTED) — both + renders pass legibility check + - 'Interactive features are genuinely altair-native: alt.selection_point crosshair + + transform_filter + tooltip layer composition' + - Zero baseline and ±1.0 clipping threshold lines are clean design refinements + - 'Data is highly realistic: A440 fundamental with harmonics, proper attack-sustain-release + envelope, realistic clipping segment' + - All font sizes explicitly set (title=16, subtitle=10, axis label=12, tick label=10) + - Canvas correctly sized at 3200×1800 with PIL pad-to-target block + - Both PNG and HTML outputs saved correctly weaknesses: - - Missing 'python' language identifier in title — must be 'waveform-audio · python - · altair · anyplot.ai' (SC-04 violation) - - 'Dark render: gradient fill''s 10% minimum opacity causes near-invisible waveform - edges against #1A1A17, making the reduced-amplitude section (0.4-0.7 s) render - as jarring dark rectangular patches — raise gradient stop floor from 0.10 to at - least 0.25-0.30 opacity' - - 'Stepped waveform appearance: abrupt amplitude transitions between dipped region - (0.4-0.7 s) and surrounding sections produce sharp rectangular holes in both renders' + - Both X and Y grid lines are present (11 horizontal + 9 vertical lines) — for a + waveform time series, Y-axis-only grid would be cleaner and less visually heavy + - Horizontal grid lines at exactly y=1.0 and y=-1.0 (the y-axis domain boundaries) + create a 'boxed top/bottom' visual effect; slight y-domain padding (e.g. domain=[-1.05, + 1.05]) would give the waveform breathing room + - No visual annotation distinguishing the amplitude dip region (0.4–0.7 s) — a subtle + annotation or legend callout could help viewers understand the dynamics phases + beyond what the subtitle states image_description: |- Light render (plot-light.png): - Background: Warm off-white (#FAF8F1) — correct - Chrome: Title "waveform-audio · altair · anyplot.ai" bold and readable; subtitle in muted ink; Y-axis "Amplitude" and X-axis "Time (seconds)" legible; tick labels in dark ink - Data: Brand green (#009E73) gradient fill for waveform envelope (10% opacity at edges, 60% at centre); matte red (#AE3030) overlay for clipped region ~0.15-0.25s; dashed zero-line and faint red clip-threshold lines at ±1.0; rectangular background patches visible in reduced-amplitude 0.4-0.7s section - Legibility verdict: PASS + Background: Warm off-white (#FAF8F1) — correct, clearly distinct from pure white + Chrome: Title "waveform-audio · python · altair · anyplot.ai" in bold dark ink, subtitle in lighter INK_MUTED, axis label "Time (seconds)" on x-axis and "Amplitude" on y-axis — all clearly readable against light background + Data: Main waveform as a teal/green filled area (#009E73 with gradient opacity 0.28–0.60) symmetric around zero; clipped region (~0.15–0.25 s) overlaid in brownish-red (#AE3030, 50% opacity); dashed zero baseline at y=0; subtle dashed threshold lines at y=±1.0; green fills from amplitude -0.8 to +0.8 at peak, narrowing to ±0.4 during the amplitude dip (0.40–0.70 s) and tapering to zero in the release + Legibility verdict: PASS — all text clearly readable, no light-on-light issues Dark render (plot-dark.png): - Background: Near-black (#1A1A17) — correct - Chrome: Title, subtitle, axis labels, tick labels all in light ink — readable; no dark-on-dark text failures - Data: Colours identical to light render (green waveform, red clip overlay); in reduced-amplitude 0.4-0.7s section, gradient's 10% opacity floor becomes nearly invisible against dark background, creating jarring dark rectangular artefact patches that look like rendering errors - Legibility verdict: PASS (text only) — waveform rendering in dark theme has artefacts in low-amplitude section + Background: Warm near-black (#1A1A17) — correct, clearly distinct from pure black + Chrome: Title in near-white (#F0EFE8), subtitle in lighter gray, axis labels and tick labels all in light INK_SOFT tones — all clearly readable against dark background; no dark-on-dark failures detected + Data: Identical brand green (#009E73) gradient fill waveform and identical #AE3030 clipping overlay compared to light render — data colors unchanged as required; zero line and threshold lines visible with light-colored dashes + Legibility verdict: PASS — all chrome correctly flips to light-on-dark, data colors identical to light render criteria_checklist: visual_quality: - score: 27 + score: 30 max: 30 items: - id: VQ-01 name: Text Legibility - score: 7 + score: 8 max: 8 passed: true - comment: All text readable in both themes; minor deduction for dark render - confusing waveform patches reducing overall readability impression + comment: All font sizes explicitly set (title=16, subtitle=10, axis title=12, + tick=10); proportional, readable in both themes - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: No text or element collisions + comment: No overlapping text elements in either render - id: VQ-03 name: Element Visibility - score: 4 + score: 6 max: 6 - passed: false - comment: Waveform clearly visible on light; dark render has jarring near-black - rectangular patches in 0.4-0.7s section where 10% opacity gradient edges - vanish against dark background + passed: true + comment: Waveform clearly visible; gradient fill effective; clipping overlay + distinct; reference lines visible - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Red/green used for clip overlay vs main waveform; position encodes - clipping redundantly so CVD-safe + comment: Green and matte-red are perceptually distinct under CVD; not sole + distinguishing signal - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: 3200x1800 confirmed, proportions good, no overflow + comment: 3200x1800 canvas correctly produced; plot fills canvas proportionally; + balanced margins - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Amplitude and Time (seconds) are descriptive with units + comment: Time (seconds) on x-axis with unit; Amplitude on y-axis is descriptive + for a normalized signal - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: '#009E73 first series; #AE3030 semantic red for clipping; backgrounds - #FAF8F1 / #1A1A17 correct' + comment: 'Main series uses #009E73 (brand green); clipping uses #AE3030 (semantic + anchor); backgrounds #FAF8F1/#1A1A17 correct; theme chrome flips correctly' design_excellence: score: 13 max: 20 @@ -107,24 +114,27 @@ review: score: 5 max: 8 passed: true - comment: Gradient fill is thoughtful; semantic red for clipping adds hierarchy; - dark-theme rendering artefacts undercut sophistication + comment: 'Above defaults: vertical gradient fill with center-dense opacity + is intentional and effective; semantic red for clipping; subtitle adds context + — but overall still ''well-configured altair'' rather than publication-ready' - id: DE-02 name: Visual Refinement score: 4 max: 6 passed: true - comment: Grid at 15% opacity subtle; dashed zero-line and clip threshold lines - add structure; stepped waveform silhouette aesthetically rough + comment: 'Clear refinements: dashed zero baseline, clipping threshold lines, + gridOpacity=0.15, strokeWidth=0 on view — but both X and Y grid lines present + (heavier than minimalist ideal)' - id: DE-03 name: Data Storytelling score: 4 max: 6 passed: true - comment: Clipping highlighted effectively; subtitle names key features; ASR - envelope visible in shape; dark artefacts weaken story + comment: 'Clear focal point: red clipping region immediately draws attention; + attack-sustain-release dynamics visible; subtitle labels all features — + good visual hierarchy' spec_compliance: - score: 13 + score: 15 max: 15 items: - id: SC-01 @@ -132,54 +142,54 @@ review: score: 5 max: 5 passed: true - comment: 'Correct waveform type: filled area symmetric around zero with min/max - envelope rendering' + comment: Correct audio waveform as filled area with min/max envelope rendering - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Semi-transparent fill, zero line, time x-axis, amplitude +-1.0, min/max - envelope, synthetic data all present + comment: 'All spec features: filled area symmetric around zero, semi-transparent + fill, zero baseline, time on x, amplitude on y, min/max envelope, synthetic + data' - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: X=time in seconds, Y=normalized amplitude -1 to +1, full data visible + comment: Time (seconds) on x-axis, normalized amplitude -1.0 to 1.0 on y-axis - id: SC-04 name: Title & Legend - score: 1 + score: 3 max: 3 - passed: false - comment: Title reads 'waveform-audio · altair · anyplot.ai' — missing required - 'python' language identifier; must be 'waveform-audio · python · altair - · anyplot.ai' + passed: true + comment: Title is exactly 'waveform-audio · python · altair · anyplot.ai'; + informative subtitle; no legend needed (appropriate for this single-waveform + chart) data_quality: - score: 14 + score: 15 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 5 + score: 6 max: 6 passed: true - comment: Attack, sustain, release, clipping, harmonics all present; envelope - rendering is spec-correct + comment: Shows attack, sustain, release, clipping segment, amplitude dip, + fundamental with 2nd and 3rd harmonics - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: 440 Hz A-note with harmonics, ASR envelope, 22050 Hz sample rate, - normalized amplitude — convincingly realistic + comment: A440 musical note with harmonics = real audio engineering scenario; + clipping = realistic issue in recordings - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: 1.5-second clip at 22050 Hz, 33075 samples, amplitude -1 to +1 — - all appropriate + comment: Normalized amplitude -1.0 to 1.0; 22050 Hz sample rate; 1.5 s duration + — all realistic and plausible code_quality: score: 10 max: 10 @@ -189,61 +199,63 @@ review: score: 3 max: 3 passed: true - comment: Flat script, no functions or classes + comment: Clean imports → data → plot → save structure; no functions or classes - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Deterministic mathematical signal, no random seed needed + comment: Fully deterministic — no random values used - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: os, altair, numpy, pandas, PIL — all used + comment: All 5 imports (os, altair, numpy, pandas, PIL.Image) are used - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean layer composition, transform_filter usage, proper noqa annotation + comment: Clean, Pythonic altair layer composition; appropriate complexity + for the visualization - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot-{THEME}.png and plot-{THEME}.html using current Altair - API + comment: Saves plot-{THEME}.png and plot-{THEME}.html; uses current altair + 6.1.0 API library_mastery: - score: 8 + score: 9 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 4 + score: 5 max: 5 passed: true - comment: Layer composition, alt.Title with subtitle, configure_* pattern, - selection params — idiomatic throughout + comment: Expert use of layer composition, encoding types, configure_* methods, + transform_filter, alt.Title with subtitle - id: LM-02 name: Distinctive Features score: 4 max: 5 passed: true - comment: Vega-Lite linear gradient fill, nearest-point selection crosshair, - interactive HTML export, transform_filter — distinctly Altair/Vega-Lite - features + comment: alt.Gradient on mark_area, alt.selection_point with nearest+pointerover, + transform_filter on datum condition — genuinely altair/vega-lite distinctive; + HTML export verdict: APPROVED impl_tags: dependencies: - pillow techniques: - hover-tooltips - - layer-composition - html-export + - layer-composition patterns: - data-generation + - matrix-construction dataprep: - binning styling: