Skip to content

feat(altair): implement waveform-audio#8291

Merged
MarkusNeusinger merged 6 commits into
mainfrom
implementation/waveform-audio/altair
Jun 3, 2026
Merged

feat(altair): implement waveform-audio#8291
MarkusNeusinger merged 6 commits into
mainfrom
implementation/waveform-audio/altair

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

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

Implementation: waveform-audio - python/altair

Implements the python/altair version of waveform-audio.

File: plots/waveform-audio/implementations/python/altair.py

Parent Issue: #4563


🤖 impl-generate workflow

github-actions Bot added 2 commits June 3, 2026 01:14
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
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 3, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white (#FAF8F1) background. Title "waveform-audio · altair · anyplot.ai" is bold and clearly readable, with a subtitle in muted ink. Y-axis label "Amplitude" and X-axis label "Time (seconds)" are both legible at appropriate sizes. Tick labels in dark ink are sharp and unobstructed. The waveform fills the chart as a symmetric teal-green (#009E73) gradient area, with higher opacity at the centre and 10% opacity at the outer edges. A clipped region (t≈0.15–0.25 s) is overlaid in semi-transparent matte red (#AE3030). A dashed zero-line and faint red clip-threshold lines at ±1.0 are present. In the 0.4–0.7 s section the amplitude envelope drops to ~50%, revealing white rectangular background patches above and below the waveform — visually confusing but technically correct. All text is readable against the light background. Legibility: PASS

Dark render (plot-dark.png): Near-black (#1A1A17) background. Title, subtitle, axis labels, and tick labels all render in light ink and are clearly readable. Data colours (teal green, matte red) are unchanged from the light render. However, in the reduced-amplitude region (0.4–0.7 s), the gradient's 10% opacity edges become nearly invisible against the dark background, and the background bleeds through as very dark rectangular patches inside the chart area — creating a visual appearance of broken/missing data. The effect is more jarring than in the light render. No dark-on-dark text failures, but the patch artefacts significantly reduce the plot's visual quality on dark surfaces. Legibility: PASS (text only); visual rendering in dark theme has artefacts that need addressing

Both renders were inspected. Data colours are identical between themes; only chrome flips.

Score: 85/100

Category Score Max
Visual Quality 27 30
Design Excellence 13 20
Spec Compliance 13 15
Data Quality 14 15
Code Quality 10 10
Library Mastery 8 10
Total 85 100

Visual Quality (27/30)

  • VQ-01: Text Legibility (7/8) — All text readable in both themes; minor deduction for the dark render's confusing waveform patches reducing overall readability impression
  • VQ-02: No Overlap (6/6) — No text or element collisions
  • VQ-03: Element Visibility (4/6) — Waveform clearly visible on light render; dark render has jarring near-black rectangular patches in the 0.4–0.7 s section where the low-opacity gradient edges disappear against the dark background
  • VQ-04: Color Accessibility (2/2) — Red/green used for clip overlay vs. main waveform; position encodes clipping redundantly so CVD-safe
  • VQ-05: Layout & Canvas (4/4) — 3200×1800 canvas confirmed, proportions good, no overflow, title fits comfortably
  • VQ-06: Axis Labels & Title (2/2) — "Amplitude" and "Time (seconds)" are descriptive and include units
  • VQ-07: Palette Compliance (2/2) — #009E73 first series ✓; #AE3030 semantic red for clipping ✓; backgrounds #FAF8F1 / #1A1A17 ✓

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) — The vertical gradient fill on the waveform is a thoughtful design choice; semantic red for clipping adds meaningful visual hierarchy; however the execution creates dark-theme rendering artefacts that undercut the sophistication
  • DE-02: Visual Refinement (4/6) — Grid at 15% opacity is subtle; dashed zero-line and clip threshold lines add structure; clean axis styling; the stepped waveform silhouette is aesthetically rough
  • DE-03: Data Storytelling (4/6) — Clipping region is effectively highlighted; subtitle names the key features; ASR envelope visible in the overall shape; dark-render artefacts weaken the story in that theme

Spec Compliance (13/15)

  • SC-01: Plot Type (5/5) — Correct waveform type: filled area symmetric around zero with min/max envelope rendering
  • SC-02: Required Features (4/4) — Semi-transparent fill ✓; horizontal zero line ✓; time x-axis ✓; amplitude ±1.0 ✓; min/max envelope ✓; synthetic data ✓
  • SC-03: Data Mapping (3/3) — X = time in seconds, Y = normalized amplitude −1 to +1, full data visible
  • SC-04: Title & Legend (1/3) — Title reads "waveform-audio · altair · anyplot.ai" — missing the required language identifier. Must be "waveform-audio · python · altair · anyplot.ai"

Data Quality (14/15)

  • DQ-01: Feature Coverage (5/6) — Attack, sustain, release, clipping, harmonics all present; could show individual waveform cycles at a zoomed-in inset for richer feature display, but envelope rendering is spec-correct
  • DQ-02: Realistic Context (5/5) — 440 Hz A-note with 2nd/3rd harmonics, ASR envelope, realistic 22050 Hz sample rate, normalized amplitude — convincingly realistic
  • DQ-03: Appropriate Scale (4/4) — 1.5-second clip at 22050 Hz, 33075 samples, amplitude −1 to +1 — all appropriate

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Flat script, no functions or classes
  • CQ-02: Reproducibility (2/2) — Deterministic mathematical signal, no random seed needed
  • CQ-03: Clean Imports (2/2) — os, altair, numpy, pandas, PIL — all used
  • CQ-04: Code Elegance (2/2) — Clean layer composition, transform_filter usage, proper noqa annotation
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html using current Altair API

Library Mastery (8/10)

  • LM-01: Idiomatic Usage (4/5) — Layer composition, alt.Title with subtitle, configure_* pattern, selection params — idiomatic throughout
  • LM-02: Distinctive Features (4/5) — Vega-Lite linear gradient fill, nearest-point selection crosshair, interactive HTML export, transform_filter — distinctly Altair/Vega-Lite features

Score Caps Applied

  • None

Strengths

  • Correct use of Altair's gradient fill API to render the waveform with #009E73 brand green as the primary series colour
  • Semantic red (#AE3030) used appropriately for clipping overlay — the strongest visual storytelling element
  • Full spec coverage: zero line, ±1.0 clip threshold lines, min/max envelope binning, ASR envelope, synthetic data
  • Interactive crosshair with tooltips leverages Altair's distinctive interactivity; HTML export saved alongside PNG
  • Completely KISS code structure with proper theme-adaptive chrome tokens throughout
  • Correct PIL padding to exact 3200×1800 canvas with the raise-on-overshoot guard

Weaknesses

  • Missing "python" language identifier in title — must be "waveform-audio · python · altair · anyplot.ai" (SC-04 violation)
  • Dark render: the gradient fill's 10% minimum opacity causes near-invisible waveform edges against the dark background (#1A1A17), making the reduced-amplitude section (0.4–0.7 s) render as jarring dark rectangular patches. Fix: raise the gradient stop floor from 0.10 to at least 0.25–0.30 opacity so the waveform area remains visually present in dark mode
  • Stepped waveform appearance: abrupt amplitude transitions between the dipped region (0.4–0.7 s) and surrounding sections produce sharp rectangular "holes" in both renders; consider smoothing the envelope transition or using a higher bin count to reduce visual stair-stepping

Issues Found

  1. SC-04 VIOLATION: Title missing language — use "waveform-audio · python · altair · anyplot.ai"
    • Fix: Change the title string in .properties() to include · python · between spec-id and library
  2. VQ-03 DARK RENDER: Gradient floor too low — 10% opacity edges vanish on dark background, creating dark artefact patches
    • Fix: Raise alt.GradientStop(color="rgba(0, 158, 115, 0.10)", ...) floor stops to rgba(0, 158, 115, 0.28) or higher at offset=0 and offset=1

AI Feedback for Next Attempt

Fix the title first: add "· python ·" between spec-id and library name. Then address the dark-render gradient issue by raising the minimum opacity of the gradient stops from 0.10 to ~0.28 so the waveform area remains visible at both amplitude extremes against the dark background. Optionally smooth the envelope transitions by applying a small Gaussian window to the envelope array to avoid the sharp rectangular step at 0.4 s and 0.7 s.

Verdict: APPROVED

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

github-actions Bot commented Jun 3, 2026

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

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


🤖 impl-repair

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 3, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): Warm off-white background (#FAF8F1). Bold dark-ink title "waveform-audio · python · altair · anyplot.ai" is clearly readable, with a lighter gray subtitle "440 Hz tone with harmonics · attack–sustain–release envelope · clipped region highlighted". The waveform is rendered as a filled area in brand green (#009E73) with a vertical gradient (opacity 0.28 at amplitude extremes → 0.60 at center), symmetric around a dashed zero baseline. A brownish-red (#AE3030) filled overlay marks the clipping region (~0.15–0.25 s). Subtle dashed threshold lines appear at ±1.0. The waveform expands from zero during the attack, shows a clear amplitude dip (~0.4–0.7 s), then a sustained section, and tapers off during release. X-axis label "Time (seconds)" and Y-axis label "Amplitude" are both readable. All text is readable against the light background — no legibility failures.

Dark render (plot-dark.png): Warm near-black background (#1A1A17). Title, subtitle, axis labels, and tick labels all render in light tones (#F0EFE8 / #B8B7B0) — all clearly readable against the dark surface. No dark-on-dark failures. Data colors are identical to the light render: same #009E73 gradient waveform fill, same #AE3030 clipping overlay. Grid lines appear as subtle light-gray lines (INK applied at 15% opacity in dark theme), and the dashed zero line and threshold lines remain visible. The brand green is clearly visible on the dark background.

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

Score: 92/100

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

Visual Quality (30/30)

  • VQ-01: Text Legibility (8/8) — All font sizes explicitly set (title=16, subtitle=10, axis=12, tick=10); proportional and readable in both themes
  • VQ-02: No Overlap (6/6) — No overlapping text elements in either render
  • VQ-03: Element Visibility (6/6) — Waveform clearly visible; gradient fill effective; clipping overlay distinct; reference lines readable
  • VQ-04: Color Accessibility (2/2) — Brand green and matte red are CVD-distinct; not sole distinguishing signal
  • VQ-05: Layout & Canvas (4/4) — 3200×1800 correctly produced with PIL pad; proportional layout, balanced margins
  • VQ-06: Axis Labels & Title (2/2) — "Time (seconds)" with unit on x-axis; "Amplitude" is descriptive for a normalized signal
  • VQ-07: Palette Compliance (2/2) — Main series #009E73; clipping #AE3030 semantic anchor correct; backgrounds #FAF8F1/#1A1A17; chrome flips correctly

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) — Above defaults: vertical gradient opacity taper on mark_area is intentional and effective; semantic red for clipping is thoughtful; subtitle adds context — but overall still "well-configured altair" rather than FiveThirtyEight publication-ready
  • DE-02: Visual Refinement (4/6) — Clear refinements: dashed zero baseline, clipping threshold lines at ±1.0, gridOpacity=0.15, strokeWidth=0 on view — but both X and Y grid lines present (heavier than minimalist ideal for a time-series waveform)
  • DE-03: Data Storytelling (4/6) — Clear focal point: red clipping region immediately draws the eye; attack–sustain–dip–release dynamics are all visible; subtitle labels all features — good visual hierarchy

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct audio waveform as filled area with min/max envelope rendering per spec
  • SC-02: Required Features (4/4) — All spec features present: filled area symmetric around zero, semi-transparent fill, zero baseline, min/max envelope, time on x-axis, amplitude on y-axis, synthetic audio data
  • SC-03: Data Mapping (3/3) — Time (seconds) on x-axis, normalized amplitude −1.0 to 1.0 on y-axis; full data range visible
  • SC-04: Title & Legend (3/3) — Exact title format "waveform-audio · python · altair · anyplot.ai"; descriptive subtitle; no legend needed (single-waveform chart)

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Shows attack, sustain, amplitude dip, release, clipping segment, 440 Hz fundamental with 2nd and 3rd harmonics
  • DQ-02: Realistic Context (5/5) — A440 musical note with harmonics is a real audio engineering scenario; clipping is a realistic recording issue
  • DQ-03: Appropriate Scale (4/4) — Normalized amplitude −1.0 to 1.0; 22050 Hz sample rate; 1.5 s duration — all plausible

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Clean imports → data → plot → save; no functions or classes
  • CQ-02: Reproducibility (2/2) — Fully deterministic signal generation; no random values
  • CQ-03: Clean Imports (2/2) — All 5 imports (os, altair, numpy, pandas, PIL.Image) are used
  • CQ-04: Code Elegance (2/2) — Clean, Pythonic altair layer composition; appropriate complexity
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html; current altair 6.1.0 API

Library Mastery (9/10)

  • LM-01: Idiomatic Usage (5/5) — Expert use of layer composition, encoding types, configure_* methods, transform_filter, alt.Title with subtitle
  • LM-02: Distinctive Features (4/5) — alt.Gradient on mark_area, alt.selection_point(nearest=True, on="pointerover") crosshair, transform_filter on datum condition, HTML export — genuinely altair/vega-lite distinctive features

Score Caps Applied

  • None — DE-01=5 and DE-02=4 both exceed the cap threshold; no other caps triggered

Strengths

  • Vertical gradient opacity taper on the waveform fill (0.28→0.60→0.28) gives natural visual depth
  • Semantic use of Imprint matte red (#AE3030) for the clipping overlay is intentional and correct
  • 600-bin min/max envelope binning correctly avoids sub-pixel aliasing artifacts
  • Full theme-adaptive chrome — both renders pass the legibility check with no dark-on-dark failures
  • Interactive crosshair + tooltip layer composition uses genuinely altair-native patterns
  • Zero baseline and ±1.0 threshold reference lines are clean, well-calibrated refinements
  • A440 with harmonics + attack–sustain–release + clipping = highly realistic and informative synthetic data
  • PIL pad-to-target block correctly handles the 3200×1800 canvas requirement

Weaknesses

  • Both X and Y grid lines are active (11 horizontal + 9 vertical lines) — for a waveform time series, Y-axis-only grid would be cleaner; consider configure_axis(grid=False) on the x-axis
  • Horizontal grid lines at exactly y=±1.0 (the y-axis domain boundaries) create a "boxed" visual effect; a small domain padding like scale=alt.Scale(domain=[-1.05, 1.05]) would give the waveform breathing room at the top and bottom edges
  • The amplitude dip region (0.4–0.7 s) is visible but unlabeled — a subtle annotation could help viewers understand the dynamics phases beyond what the subtitle states

Issues Found

  1. DE-02 MODERATE: Bi-directional grid (both X and Y) is heavier than ideal for this plot type
    • Fix: Disable X-axis grid (configure_axis for x or use separate configure on the x layer) — retain Y-axis grid for amplitude reading
  2. DE-01 MODERATE: Strong but not publication-ready — the gradient is the standout element; everything else is well-configured defaults
    • Fix: Consider removing the visible axis domain lines or adding a subtle chart area fill differentiation

AI Feedback for Next Attempt

This implementation is solid — gradient fill, semantic clipping color, and interactive features are all working well. The main improvement area is visual refinement: switch to Y-axis-only grid (remove vertical grid lines), add a small y-domain padding to avoid the "boxed" grid-at-boundary effect, and consider removing the x-axis domain line for a cleaner L-shaped frame. These changes would push DE-02 from 4 to 6.

Verdict: APPROVED

@github-actions github-actions Bot added quality:92 Quality score 92/100 ai-approved Quality OK, ready for merge and removed quality:85 Quality score 85/100 labels Jun 3, 2026
@MarkusNeusinger MarkusNeusinger merged commit 315e973 into main Jun 3, 2026
3 checks passed
@MarkusNeusinger MarkusNeusinger deleted the implementation/waveform-audio/altair branch June 3, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant