From 8f53ce7452dd0f71bbc461ba4acd022e7db73cd4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 18:11:30 +0000 Subject: [PATCH 1/5] feat(ggplot2): implement spectrogram-mel --- .../implementations/r/ggplot2.R | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 plots/spectrogram-mel/implementations/r/ggplot2.R diff --git a/plots/spectrogram-mel/implementations/r/ggplot2.R b/plots/spectrogram-mel/implementations/r/ggplot2.R new file mode 100644 index 0000000000..1d945f9661 --- /dev/null +++ b/plots/spectrogram-mel/implementations/r/ggplot2.R @@ -0,0 +1,147 @@ +#' anyplot.ai +#' spectrogram-mel: Mel-Spectrogram for Audio Analysis +#' Library: ggplot2 | R 4.x +#' Quality: pending | Created: 2026-06-03 + +library(ggplot2) +library(dplyr) +library(scales) +library(ragg) + +set.seed(42) + +# --- Theme tokens --- +THEME <- Sys.getenv("ANYPLOT_THEME", "light") +PAGE_BG <- if (THEME == "light") "#FAF8F1" else "#1A1A17" +ELEVATED_BG <- if (THEME == "light") "#FFFDF6" else "#242420" +INK <- if (THEME == "light") "#1A1A17" else "#F0EFE8" +INK_SOFT <- if (THEME == "light") "#4A4A44" else "#B8B7B0" + +# --- Audio parameters --- +sample_rate <- 22050L +duration <- 4.0 +n_fft <- 2048L +hop_length <- 512L +n_mels <- 128L + +# --- Synthesize audio: C-major arpeggio with harmonics and pluck-style envelope --- +n_samples <- as.integer(sample_rate * duration) +t_vec <- seq(0, duration, length.out = n_samples + 1L)[seq_len(n_samples)] +note_freqs <- c(261.63, 329.63, 392.00, 523.25, 659.25, 523.25, 392.00, 329.63) +note_dur <- duration / length(note_freqs) + +audio <- numeric(n_samples) +for (i in seq_along(note_freqs)) { + t0 <- (i - 1L) * note_dur + t1 <- i * note_dur + idx <- which(t_vec >= t0 & t_vec < t1) + f <- note_freqs[i] + dt <- t_vec[idx] - t0 + env <- pmax(0, (1 - exp(-300 * dt)) * exp(-4 * dt)) + audio[idx] <- env * ( + 0.60 * sin(2 * pi * f * t_vec[idx]) + + 0.25 * sin(2 * pi * 2 * f * t_vec[idx]) + + 0.10 * sin(2 * pi * 3 * f * t_vec[idx]) + + 0.05 * sin(2 * pi * 4 * f * t_vec[idx]) + ) +} +audio <- audio + 0.008 * rnorm(n_samples) + +# --- STFT: Hann-windowed power spectrogram --- +n_fft_half <- n_fft %/% 2L + 1L +hann_win <- 0.5 * (1 - cos(2 * pi * seq(0L, n_fft - 1L) / (n_fft - 1L))) +n_frames <- floor((n_samples - n_fft) / hop_length) + 1L + +stft_power <- matrix(0.0, nrow = n_fft_half, ncol = n_frames) +for (i in seq_len(n_frames)) { + s <- (i - 1L) * hop_length + 1L + stft_power[, i] <- Mod(fft(audio[s:(s + n_fft - 1L)] * hann_win)[seq_len(n_fft_half)])^2 +} + +# --- Mel filterbank --- +hz_to_mel <- function(f) 2595 * log10(1 + f / 700) +mel_to_hz <- function(m) 700 * (10^(m / 2595) - 1) + +f_min <- 80.0 +f_max <- as.numeric(sample_rate) / 2.0 +mel_pts <- seq(hz_to_mel(f_min), hz_to_mel(f_max), length.out = n_mels + 2L) +hz_pts <- mel_to_hz(mel_pts) +fft_freqs <- seq(0, f_max, length.out = n_fft_half) + +mel_fb <- matrix(0.0, nrow = n_mels, ncol = n_fft_half) +for (m in seq_len(n_mels)) { + rising <- (fft_freqs - hz_pts[m]) / (hz_pts[m + 1L] - hz_pts[m]) + falling <- (hz_pts[m + 2L] - fft_freqs) / (hz_pts[m + 2L] - hz_pts[m + 1L]) + mel_fb[m, ] <- pmax(0.0, pmin(rising, falling)) +} + +# --- Mel spectrogram in dB, normalized to 0 dB peak --- +mel_spec <- mel_fb %*% stft_power +mel_spec_db <- 10 * log10(mel_spec + 1e-10) +mel_spec_db <- mel_spec_db - max(mel_spec_db) + +# --- Long-format data frame --- +mel_centers <- mel_to_hz(mel_pts[2:(n_mels + 1L)]) +time_axis <- ((seq_len(n_frames) - 1L) * hop_length + n_fft / 2L) / sample_rate + +grid_idx <- expand.grid(mel_band = seq_len(n_mels), time_idx = seq_len(n_frames)) +df <- data.frame( + time_s = time_axis[grid_idx$time_idx], + mel_band = grid_idx$mel_band, + db = mel_spec_db[cbind(grid_idx$mel_band, grid_idx$time_idx)] +) + +# Y-axis: key frequency labels at representative mel-band positions +key_freqs <- c(100, 250, 500, 1000, 2000, 4000, 8000) +key_bands <- sapply(key_freqs, function(f) which.min(abs(mel_centers - f))) +key_labels <- ifelse(key_freqs >= 1000, paste0(key_freqs / 1000, "k Hz"), paste0(key_freqs, " Hz")) + +title_str <- "spectrogram-mel · r · ggplot2 · anyplot.ai" + +# --- Plot --- +p <- ggplot(df, aes(x = time_s, y = mel_band, fill = db)) + + geom_tile() + + scale_fill_gradient( + name = "dB", + low = "#009E73", + high = "#4467A3", + limits = c(-80, 0), + oob = scales::squish + ) + + scale_x_continuous( + name = "Time (s)", + expand = c(0, 0) + ) + + scale_y_continuous( + name = "Frequency (Hz)", + breaks = key_bands, + labels = key_labels, + expand = c(0, 0) + ) + + labs(title = title_str) + + theme_minimal(base_size = 8) + + theme( + plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG), + panel.background = element_rect(fill = PAGE_BG, color = NA), + panel.grid.major = element_blank(), + panel.grid.minor = element_blank(), + panel.border = element_rect(color = INK_SOFT, fill = NA, linewidth = 0.5), + axis.title = element_text(color = INK, size = 10), + axis.text = element_text(color = INK_SOFT, size = 8), + plot.title = element_text(color = INK, size = 12, face = "bold"), + legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.3), + legend.text = element_text(color = INK_SOFT, size = 8), + legend.title = element_text(color = INK, size = 10), + plot.margin = margin(12, 12, 12, 12) + ) + +# --- Save --- +ggsave( + filename = sprintf("plot-%s.png", THEME), + plot = p, + device = ragg::agg_png, + width = 8, + height = 4.5, + units = "in", + dpi = 400 +) From 0a468cce3b155d778d264157a951bc3de47229f9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 18:11:43 +0000 Subject: [PATCH 2/5] chore(ggplot2): add metadata for spectrogram-mel --- plots/spectrogram-mel/metadata/r/ggplot2.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plots/spectrogram-mel/metadata/r/ggplot2.yaml diff --git a/plots/spectrogram-mel/metadata/r/ggplot2.yaml b/plots/spectrogram-mel/metadata/r/ggplot2.yaml new file mode 100644 index 0000000000..6076b5198a --- /dev/null +++ b/plots/spectrogram-mel/metadata/r/ggplot2.yaml @@ -0,0 +1,21 @@ +# Per-library metadata for ggplot2 implementation of spectrogram-mel +# Auto-generated by impl-generate.yml + +library: ggplot2 +language: r +specification_id: spectrogram-mel +created: '2026-06-03T18:11:42Z' +updated: '2026-06-03T18:11:42Z' +generated_by: claude-sonnet +workflow_run: 26903575433 +issue: 4672 +language_version: 4.4.1 +library_version: 3.5.1 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/spectrogram-mel/r/ggplot2/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/spectrogram-mel/r/ggplot2/plot-dark.png +preview_html_light: null +preview_html_dark: null +quality_score: null +review: + strengths: [] + weaknesses: [] From 81c4a7327b59075783a4df77a5adb23bcbacadd4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 18:17:53 +0000 Subject: [PATCH 3/5] chore(ggplot2): update quality score 83 and review feedback for spectrogram-mel --- .../implementations/r/ggplot2.R | 4 +- plots/spectrogram-mel/metadata/r/ggplot2.yaml | 253 +++++++++++++++++- 2 files changed, 248 insertions(+), 9 deletions(-) diff --git a/plots/spectrogram-mel/implementations/r/ggplot2.R b/plots/spectrogram-mel/implementations/r/ggplot2.R index 1d945f9661..e41702d292 100644 --- a/plots/spectrogram-mel/implementations/r/ggplot2.R +++ b/plots/spectrogram-mel/implementations/r/ggplot2.R @@ -1,7 +1,7 @@ #' anyplot.ai #' spectrogram-mel: Mel-Spectrogram for Audio Analysis -#' Library: ggplot2 | R 4.x -#' Quality: pending | Created: 2026-06-03 +#' Library: ggplot2 3.5.1 | R 4.4.1 +#' Quality: 83/100 | Created: 2026-06-03 library(ggplot2) library(dplyr) diff --git a/plots/spectrogram-mel/metadata/r/ggplot2.yaml b/plots/spectrogram-mel/metadata/r/ggplot2.yaml index 6076b5198a..a618b3c304 100644 --- a/plots/spectrogram-mel/metadata/r/ggplot2.yaml +++ b/plots/spectrogram-mel/metadata/r/ggplot2.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for ggplot2 implementation of spectrogram-mel -# Auto-generated by impl-generate.yml - library: ggplot2 language: r specification_id: spectrogram-mel created: '2026-06-03T18:11:42Z' -updated: '2026-06-03T18:11:42Z' +updated: '2026-06-03T18:17:53Z' generated_by: claude-sonnet workflow_run: 26903575433 issue: 4672 @@ -15,7 +12,249 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/spectrogr preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/spectrogram-mel/r/ggplot2/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: null +quality_score: 83 review: - strengths: [] - weaknesses: [] + strengths: + - Complete from-scratch mel filterbank and STFT implementation without external + audio libraries (librosa) — impressive for R/ggplot2 + - 'Full spec compliance: all required features (mel scale, dB colorbar, Hz tick + labels, synthesized audio, correct parameters) implemented correctly' + - Theme-adaptive chrome correctly wired through all elements; both renders pass + readability check with no dark-on-dark failures + - 'Palette compliance: imprint_seq (#009E73 → #4467A3) is the correct Imprint continuous + colormap for single-polarity data' + - Clean, flat script structure with clear sections and minimal helper functions + weaknesses: + - 'VQ-03: The imprint_seq colormap (teal-green to blue) has limited luminance contrast; + the upper ~65% of the plot area is uniformly teal/green (−80 dB region) with no + visible spectral structure due to synthesized audio concentrating energy below + 2k Hz. Consider synthesizing audio with more high-frequency content to better + utilize the full frequency axis' + - 'DE-01/DE-03: No visual storytelling or emphasis — the arpeggio note structure + is visible but the viewer has no guidance; a simple annotation marking note onsets + or a horizontal reference line at the fundamental frequency range would significantly + improve the design' + - 'DQ-01: Synthesized signal has most energy below 2 kHz, leaving ~65% of the mel + bands uniformly at −80 dB; for a 128-band mel-spectrogram demo this wastes the + frequency axis. Adding higher harmonics, broadband noise, or a frequency sweep + would better demonstrate the mel scale''s perceptual compression' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct + Chrome: Bold dark-ink title "spectrogram-mel · r · ggplot2 · anyplot.ai" clearly readable; axis labels "Time (s)" and "Frequency (Hz)" readable at 10pt; tick labels (100 Hz–8k Hz, 1–3 s) readable at 8pt; colorbar title "dB" and tick labels (0 to −80) readable + Data: teal-green (#009E73) at −80 dB (low energy) to blue (#4467A3) at 0 dB (high energy) — imprint_seq. Arpeggio note pattern visible as horizontal blue/blue-teal bands in 100 Hz–2k Hz region. Upper ~65% of spectrogram (above 2k Hz) is uniformly teal-green (synthesized audio has little high-frequency energy). Colorbar on right with elevated background. + Legibility verdict: PASS + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct + Chrome: Title, axis labels, tick labels all rendered in light ink (#F0EFE8 / #B8B7B0 tokens); all clearly readable against dark background. No dark-on-dark failures. Colorbar box uses dark elevated background (#242420) with light text. + Data: Colors identical to light render — same teal-green to blue gradient, same arpeggio pattern visible in lower frequency bands. Data colors (imprint_seq) unchanged across themes as required. + Legibility verdict: PASS + criteria_checklist: + visual_quality: + score: 27 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All title, axis, tick, and legend text readable in both themes; small + deduction for colorbar tick labels being on the smaller side at scale + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No element collisions in either render + - id: VQ-03 + name: Element Visibility + score: 4 + max: 6 + passed: true + comment: Arpeggio pattern visible in lower bands but imprint_seq green-to-blue + gradient has limited luminance contrast; large uniform teal area in upper + ~65% of spectrogram shows no structural detail + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: imprint_seq is CVD-safe by design + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Correct 3200x1800 canvas, clean proportions, no clipping, legend + well-placed + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Time (s), Frequency (Hz), colorbar dB all descriptive with units + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'Sequential colormap built from #009E73 to #4467A3 (imprint_seq); + backgrounds #FAF8F1 / #1A1A17; both themes correct' + design_excellence: + score: 10 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 4 + max: 8 + passed: false + comment: Correct palette use and clean layout; no visual hierarchy, annotation, + or emphasis to guide the viewer; no design decisions beyond following the + rules + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: false + comment: No grid (appropriate for spectrograms); panel border present; reasonable + margins; could benefit from slightly more generous whitespace + - id: DE-03 + name: Data Storytelling + score: 3 + max: 6 + passed: false + comment: Note changes in arpeggio visible but large monotone upper-frequency + area reduces visual impact; no annotation or labeling to highlight what + the viewer should notice + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct mel-spectrogram using geom_tile() + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: dB scale conversion, mel filterbank from scratch, colorbar labeled + dB, Hz labels at key mel band edges + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=time in seconds, Y=mel band with Hz labels, fill=dB; all axes show + full data range + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title is spectrogram-mel · r · ggplot2 · anyplot.ai; colorbar labeled + dB + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 5 + max: 6 + passed: true + comment: Complete mel filterbank, STFT with Hann window, 128 mel bands, peak + normalization; slight deduction as synthesized signal concentrates energy + below 2k Hz leaving ~65% of plot with no spectral detail + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: C-major arpeggio with harmonics and pluck-style envelope is a realistic, + neutral audio ML example + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 4s at 22050 Hz, n_fft=2048, hop_length=512, n_mels=128, -80 to 0 + dB range — all typical parameters + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Flat script; small inline helper functions hz_to_mel/mel_to_hz are + minimal and necessary + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: set.seed(42) present + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: ggplot2, dplyr, scales, ragg all present; dplyr imported but unused + (minor, harmless) + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean section structure, no fake UI, no unnecessary complexity + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves as plot-{THEME}.png via sprintf + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: geom_tile() for spectrogram, scale_fill_gradient() for continuous + fill, scale_y_continuous() with explicit breaks for mel-band labels — all + idiomatic ggplot2 + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: false + comment: Uses ggplot2 long-format data pipeline effectively; custom Y-axis + break mapping is a nice ggplot2 pattern; does not leverage faceting, coord_trans, + or other distinctive ggplot2 capabilities + verdict: APPROVED +impl_tags: + dependencies: + - ragg + - scales + techniques: + - colorbar + - manual-ticks + patterns: + - data-generation + - matrix-construction + - iteration-over-groups + dataprep: + - normalization + styling: + - custom-colormap From a4b9f91251a113d83326615d7ea98e96bfd7d741 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 18:25:56 +0000 Subject: [PATCH 4/5] fix(ggplot2): address review feedback for spectrogram-mel Attempt 1/3 - fixes based on AI review --- .../implementations/r/ggplot2.R | 78 +++++++++++++++---- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/plots/spectrogram-mel/implementations/r/ggplot2.R b/plots/spectrogram-mel/implementations/r/ggplot2.R index e41702d292..5e1811502c 100644 --- a/plots/spectrogram-mel/implementations/r/ggplot2.R +++ b/plots/spectrogram-mel/implementations/r/ggplot2.R @@ -1,10 +1,8 @@ #' anyplot.ai #' spectrogram-mel: Mel-Spectrogram for Audio Analysis #' Library: ggplot2 3.5.1 | R 4.4.1 -#' Quality: 83/100 | Created: 2026-06-03 library(ggplot2) -library(dplyr) library(scales) library(ragg) @@ -16,6 +14,7 @@ PAGE_BG <- if (THEME == "light") "#FAF8F1" else "#1A1A17" ELEVATED_BG <- if (THEME == "light") "#FFFDF6" else "#242420" INK <- if (THEME == "light") "#1A1A17" else "#F0EFE8" INK_SOFT <- if (THEME == "light") "#4A4A44" else "#B8B7B0" +INK_MUTED <- if (THEME == "light") "#6B6A63" else "#A8A79F" # --- Audio parameters --- sample_rate <- 22050L @@ -24,28 +23,30 @@ n_fft <- 2048L hop_length <- 512L n_mels <- 128L -# --- Synthesize audio: C-major arpeggio with harmonics and pluck-style envelope --- +# --- Synthesize audio: C-major arpeggio with 8 harmonics + percussive transients --- n_samples <- as.integer(sample_rate * duration) t_vec <- seq(0, duration, length.out = n_samples + 1L)[seq_len(n_samples)] note_freqs <- c(261.63, 329.63, 392.00, 523.25, 659.25, 523.25, 392.00, 329.63) note_dur <- duration / length(note_freqs) -audio <- numeric(n_samples) +audio <- numeric(n_samples) +harm_amp <- 0.5 * 0.6^(0:7) # 8 harmonics with exponential amplitude decay + for (i in seq_along(note_freqs)) { t0 <- (i - 1L) * note_dur - t1 <- i * note_dur - idx <- which(t_vec >= t0 & t_vec < t1) + idx <- which(t_vec >= t0 & t_vec < t0 + note_dur) f <- note_freqs[i] dt <- t_vec[idx] - t0 env <- pmax(0, (1 - exp(-300 * dt)) * exp(-4 * dt)) - audio[idx] <- env * ( - 0.60 * sin(2 * pi * f * t_vec[idx]) + - 0.25 * sin(2 * pi * 2 * f * t_vec[idx]) + - 0.10 * sin(2 * pi * 3 * f * t_vec[idx]) + - 0.05 * sin(2 * pi * 4 * f * t_vec[idx]) - ) + wave <- Reduce("+", lapply(seq_along(harm_amp), function(h) { + harm_amp[h] * sin(2 * pi * h * f * t_vec[idx]) + })) + # Percussive onset burst: broadband noise decaying over ~25 ms + burst_len <- min(as.integer(0.025 * sample_rate), length(idx)) + burst_env <- c(exp(-150 * dt[seq_len(burst_len)]), numeric(length(idx) - burst_len)) + audio[idx] <- env * wave + 0.18 * burst_env * rnorm(length(idx)) } -audio <- audio + 0.008 * rnorm(n_samples) +audio <- audio + 0.012 * rnorm(n_samples) # --- STFT: Hann-windowed power spectrogram --- n_fft_half <- n_fft %/% 2L + 1L @@ -96,17 +97,54 @@ key_freqs <- c(100, 250, 500, 1000, 2000, 4000, 8000) key_bands <- sapply(key_freqs, function(f) which.min(abs(mel_centers - f))) key_labels <- ifelse(key_freqs >= 1000, paste0(key_freqs / 1000, "k Hz"), paste0(key_freqs, " Hz")) +# Annotation reference positions +note_onsets <- (seq_along(note_freqs) - 1L) * note_dur +band_1k <- which.min(abs(mel_centers - 1000)) +band_2k <- which.min(abs(mel_centers - 2000)) +t_max <- max(time_axis) + title_str <- "spectrogram-mel · r · ggplot2 · anyplot.ai" # --- Plot --- p <- ggplot(df, aes(x = time_s, y = mel_band, fill = db)) + geom_tile() + + # Note onset markers — reveal rhythmic structure of the arpeggio + geom_vline( + xintercept = note_onsets[-1], + color = INK_MUTED, + linewidth = 0.3, + linetype = "dotted" + ) + + # Perceptual boundary: 1 kHz separates fundamental region from overtones + geom_hline( + yintercept = band_1k, + color = INK_SOFT, + linewidth = 0.45, + linetype = "dashed" + ) + + annotate("text", + x = t_max * 0.97, y = band_1k + 2.5, + label = "1 kHz", color = INK_SOFT, + size = 2.3, hjust = 1 + ) + + # Frequency region labels for interpretive guidance + annotate("text", + x = 0.10, y = 5, + label = "Fundamentals", color = INK_MUTED, + size = 2.3, hjust = 0, fontface = "italic" + ) + + annotate("text", + x = 0.10, y = band_2k + 4, + label = "Overtones", color = INK_MUTED, + size = 2.3, hjust = 0, fontface = "italic" + ) + scale_fill_gradient( name = "dB", low = "#009E73", high = "#4467A3", limits = c(-80, 0), - oob = scales::squish + oob = scales::squish, + breaks = c(0, -20, -40, -60, -80) ) + scale_x_continuous( name = "Time (s)", @@ -118,21 +156,27 @@ p <- ggplot(df, aes(x = time_s, y = mel_band, fill = db)) + labels = key_labels, expand = c(0, 0) ) + - labs(title = title_str) + + guides(fill = guide_colorbar(barheight = 7, barwidth = 0.7, ticks = TRUE)) + + labs( + title = title_str, + subtitle = "C-major arpeggio · 8 harmonics · mel scale compresses perceptual distances" + ) + theme_minimal(base_size = 8) + theme( plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG), panel.background = element_rect(fill = PAGE_BG, color = NA), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), - panel.border = element_rect(color = INK_SOFT, fill = NA, linewidth = 0.5), + panel.border = element_blank(), axis.title = element_text(color = INK, size = 10), axis.text = element_text(color = INK_SOFT, size = 8), + axis.line = element_line(color = INK_SOFT, linewidth = 0.4), plot.title = element_text(color = INK, size = 12, face = "bold"), + plot.subtitle = element_text(color = INK_SOFT, size = 8), legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.3), legend.text = element_text(color = INK_SOFT, size = 8), legend.title = element_text(color = INK, size = 10), - plot.margin = margin(12, 12, 12, 12) + plot.margin = margin(16, 16, 16, 16) ) # --- Save --- From d37b0ad647f4f424c3d9db7e1a3cfc452b5192f4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jun 2026 18:32:41 +0000 Subject: [PATCH 5/5] chore(ggplot2): update quality score 88 and review feedback for spectrogram-mel --- .../implementations/r/ggplot2.R | 1 + plots/spectrogram-mel/metadata/r/ggplot2.yaml | 172 +++++++++--------- 2 files changed, 83 insertions(+), 90 deletions(-) diff --git a/plots/spectrogram-mel/implementations/r/ggplot2.R b/plots/spectrogram-mel/implementations/r/ggplot2.R index 5e1811502c..15cd11a25d 100644 --- a/plots/spectrogram-mel/implementations/r/ggplot2.R +++ b/plots/spectrogram-mel/implementations/r/ggplot2.R @@ -1,6 +1,7 @@ #' anyplot.ai #' spectrogram-mel: Mel-Spectrogram for Audio Analysis #' Library: ggplot2 3.5.1 | R 4.4.1 +#' Quality: 88/100 | Created: 2026-06-03 library(ggplot2) library(scales) diff --git a/plots/spectrogram-mel/metadata/r/ggplot2.yaml b/plots/spectrogram-mel/metadata/r/ggplot2.yaml index a618b3c304..72f9288d8e 100644 --- a/plots/spectrogram-mel/metadata/r/ggplot2.yaml +++ b/plots/spectrogram-mel/metadata/r/ggplot2.yaml @@ -2,7 +2,7 @@ library: ggplot2 language: r specification_id: spectrogram-mel created: '2026-06-03T18:11:42Z' -updated: '2026-06-03T18:17:53Z' +updated: '2026-06-03T18:32:41Z' generated_by: claude-sonnet workflow_run: 26903575433 issue: 4672 @@ -12,47 +12,44 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/spectrogr preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/spectrogram-mel/r/ggplot2/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 83 +quality_score: 88 review: strengths: - - Complete from-scratch mel filterbank and STFT implementation without external - audio libraries (librosa) — impressive for R/ggplot2 - - 'Full spec compliance: all required features (mel scale, dB colorbar, Hz tick - labels, synthesized audio, correct parameters) implemented correctly' - - Theme-adaptive chrome correctly wired through all elements; both renders pass - readability check with no dark-on-dark failures - - 'Palette compliance: imprint_seq (#009E73 → #4467A3) is the correct Imprint continuous - colormap for single-polarity data' - - Clean, flat script structure with clear sections and minimal helper functions + - Full STFT and mel filterbank implemented in pure R without librosa — impressive + DSP fidelity with no external audio dependencies + - 'imprint_seq colormap (#009E73 → #4467A3) correctly applied to continuous intensity + data per style guide rules' + - Complete theme-adaptive chrome with all tokens (INK, INK_SOFT, INK_MUTED, PAGE_BG, + ELEVATED_BG) properly flipped between themes + - Meaningful annotations (1 kHz boundary with region labels Fundamentals/Overtones, + note onset markers) guide interpretation + - 'Perfect spec compliance: dB scale, mel filterbank, colorbar labeled in dB, Hz-labeled + y-axis, time x-axis' + - Clean reproducible code with set.seed(42), no classes or unnecessary wrappers, + modern linewidth= API weaknesses: - - 'VQ-03: The imprint_seq colormap (teal-green to blue) has limited luminance contrast; - the upper ~65% of the plot area is uniformly teal/green (−80 dB region) with no - visible spectral structure due to synthesized audio concentrating energy below - 2k Hz. Consider synthesizing audio with more high-frequency content to better - utilize the full frequency axis' - - 'DE-01/DE-03: No visual storytelling or emphasis — the arpeggio note structure - is visible but the viewer has no guidance; a simple annotation marking note onsets - or a horizontal reference line at the fundamental frequency range would significantly - improve the design' - - 'DQ-01: Synthesized signal has most energy below 2 kHz, leaving ~65% of the mel - bands uniformly at −80 dB; for a 128-band mel-spectrogram demo this wastes the - frequency axis. Adding higher harmonics, broadband noise, or a frequency sweep - would better demonstrate the mel scale''s perceptual compression' + - 'Annotation text at size=2.3 mm is slightly small; ''Fundamentals'' in light render + has marginal contrast against teal tiles (#6B6A63 on #009E73 background) — raise + to size=2.8–3.0 and use INK_SOFT instead of INK_MUTED for annotations overlaying + colored tiles' + - Spectrogram dynamic range dominated by narrow energy band; data storytelling limited + — consider tightening colorbar floor from -80 dB to -60 dB to enhance contrast + in the meaningful energy region, or add a second annotated frequency boundary image_description: |- Light render (plot-light.png): - Background: Warm off-white #FAF8F1 — correct - Chrome: Bold dark-ink title "spectrogram-mel · r · ggplot2 · anyplot.ai" clearly readable; axis labels "Time (s)" and "Frequency (Hz)" readable at 10pt; tick labels (100 Hz–8k Hz, 1–3 s) readable at 8pt; colorbar title "dB" and tick labels (0 to −80) readable - Data: teal-green (#009E73) at −80 dB (low energy) to blue (#4467A3) at 0 dB (high energy) — imprint_seq. Arpeggio note pattern visible as horizontal blue/blue-teal bands in 100 Hz–2k Hz region. Upper ~65% of spectrogram (above 2k Hz) is uniformly teal-green (synthesized audio has little high-frequency energy). Colorbar on right with elevated background. - Legibility verdict: PASS + Background: Warm off-white #FAF8F1 — correct, not pure white + Chrome: Title "spectrogram-mel · r · ggplot2 · anyplot.ai" bold dark ink, ~70% plot width. Subtitle readable. Axis labels "Time (s)" and "Frequency (Hz)" in dark ink. Tick labels (100 Hz–8k Hz, 0–3 s) in INK_SOFT (#4A4A44). All readable. + Data: imprint_seq colormap (green #009E73 low → blue #4467A3 high) fills heatmap tiles. Dashed 1 kHz boundary line and dotted onset markers visible. "Fundamentals" and "Overtones" annotations present but subtle (INK_MUTED #6B6A63 on teal tiles). "1 kHz" label near right edge readable. + Legibility verdict: PASS (annotation text slightly small/low-contrast on colored tiles — minor) Dark render (plot-dark.png): - Background: Warm near-black #1A1A17 — correct - Chrome: Title, axis labels, tick labels all rendered in light ink (#F0EFE8 / #B8B7B0 tokens); all clearly readable against dark background. No dark-on-dark failures. Colorbar box uses dark elevated background (#242420) with light text. - Data: Colors identical to light render — same teal-green to blue gradient, same arpeggio pattern visible in lower frequency bands. Data colors (imprint_seq) unchanged across themes as required. + Background: Warm near-black #1A1A17 — correct, not pure black + Chrome: Title and all text flip to light ink (#F0EFE8 / #B8B7B0). All labels clearly readable. No dark-on-dark failures. "Fundamentals" and "Overtones" annotations more visible in dark mode (INK_MUTED #A8A79F reads better against dark teal). Legend uses elevated background #242420 with soft border. + Data: Colors are IDENTICAL to light render — imprint_seq green-to-blue gradient unchanged between themes. Only chrome flips. Legibility verdict: PASS criteria_checklist: visual_quality: - score: 27 + score: 29 max: 30 items: - id: VQ-01 @@ -60,75 +57,74 @@ review: score: 7 max: 8 passed: true - comment: All title, axis, tick, and legend text readable in both themes; small - deduction for colorbar tick labels being on the smaller side at scale + comment: Title 12pt, axis labels 10pt, tick labels 8pt all properly sized. + Annotation text size=2.3mm slightly small; 'Fundamentals' has marginal contrast + on teal tiles in light render. - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: No element collisions in either render + comment: No text-text or text-data overlaps in either render. - id: VQ-03 name: Element Visibility - score: 4 + score: 6 max: 6 passed: true - comment: Arpeggio pattern visible in lower bands but imprint_seq green-to-blue - gradient has limited luminance contrast; large uniform teal area in upper - ~65% of spectrogram shows no structural detail + comment: Heatmap tiles clearly visible; dotted onset lines thin but appropriate + for secondary reference. - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: imprint_seq is CVD-safe by design + comment: imprint_seq (green→blue) is CVD-safe. - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Correct 3200x1800 canvas, clean proportions, no clipping, legend - well-placed + comment: Canvas 3200x1800 passes gate. Proportions appropriate for spectrogram. + Colorbar well-positioned. - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Time (s), Frequency (Hz), colorbar dB all descriptive with units + comment: Time (s), Frequency (Hz), colorbar dB — all labeled with units. - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'Sequential colormap built from #009E73 to #4467A3 (imprint_seq); - backgrounds #FAF8F1 / #1A1A17; both themes correct' + comment: 'imprint_seq (#009E73 low, #4467A3 high). Backgrounds #FAF8F1 / #1A1A17 + correct. All chrome tokens theme-adaptive.' design_excellence: - score: 10 + score: 12 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 4 + score: 5 max: 8 - passed: false - comment: Correct palette use and clean layout; no visual hierarchy, annotation, - or emphasis to guide the viewer; no design decisions beyond following the - rules + passed: true + comment: 'Intentional design: custom imprint_seq colormap, annotated 1 kHz + boundary, region labels, percussive synthesis. Clear visual hierarchy.' - id: DE-02 name: Visual Refinement - score: 3 + score: 4 max: 6 - passed: false - comment: No grid (appropriate for spectrograms); panel border present; reasonable - margins; could benefit from slightly more generous whitespace + passed: true + comment: Grid fully removed, panel border absent, axis lines styled, legend + with elevated-background border, 16px margins. - id: DE-03 name: Data Storytelling score: 3 max: 6 - passed: false - comment: Note changes in arpeggio visible but large monotone upper-frequency - area reduces visual impact; no annotation or labeling to highlight what - the viewer should notice + passed: true + comment: 1 kHz boundary with region labels and onset markers provide interpretive + scaffolding. Subtitle gives context. Dynamic range concentration limits + visual impact. spec_compliance: score: 15 max: 15 @@ -138,54 +134,52 @@ review: score: 5 max: 5 passed: true - comment: Correct mel-spectrogram using geom_tile() + comment: Correct mel spectrogram via geom_tile on mel-scaled frequency axis. - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: dB scale conversion, mel filterbank from scratch, colorbar labeled - dB, Hz labels at key mel band edges + comment: dB conversion, mel filterbank, sequential colormap, colorbar labeled + in dB — all present. - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: X=time in seconds, Y=mel band with Hz labels, fill=dB; all axes show - full data range + comment: X=time(s), Y=mel band with Hz labels, fill=dB values. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title is spectrogram-mel · r · ggplot2 · anyplot.ai; colorbar labeled - dB + comment: 'Title: ''spectrogram-mel · r · ggplot2 · anyplot.ai''. Colorbar + labeled ''dB''.' data_quality: - score: 14 + score: 15 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 5 + score: 6 max: 6 passed: true - comment: Complete mel filterbank, STFT with Hann window, 128 mel bands, peak - normalization; slight deduction as synthesized signal concentrates energy - below 2k Hz leaving ~65% of plot with no spectral detail + comment: Full 128-band mel spectrogram, 8 harmonics per note, percussive onset + bursts, additive noise floor. - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: C-major arpeggio with harmonics and pluck-style envelope is a realistic, - neutral audio ML example + comment: C-major arpeggio is neutral, universally recognizable, with realistic + synthesis parameters. - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: 4s at 22050 Hz, n_fft=2048, hop_length=512, n_mels=128, -80 to 0 - dB range — all typical parameters + comment: 'Standard parameters: 22050 Hz SR, n_fft=2048, hop_length=512, n_mels=128, + -80 to 0 dB range.' code_quality: score: 10 max: 10 @@ -195,33 +189,32 @@ review: score: 3 max: 3 passed: true - comment: Flat script; small inline helper functions hz_to_mel/mel_to_hz are - minimal and necessary + comment: No wrapper functions or classes; hz_to_mel/mel_to_hz helpers necessary + for filterbank. - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: set.seed(42) present + comment: set.seed(42) at top. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: ggplot2, dplyr, scales, ragg all present; dplyr imported but unused - (minor, harmless) + comment: ggplot2, scales, ragg — all used. - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean section structure, no fake UI, no unnecessary complexity + comment: DSP complexity inherent to plot type; no fake UI; clean R idioms. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot-{THEME}.png via sprintf + comment: Saves plot-%s.png with THEME env var; modern linewidth= API used. library_mastery: score: 7 max: 10 @@ -231,25 +224,24 @@ review: score: 4 max: 5 passed: true - comment: geom_tile() for spectrogram, scale_fill_gradient() for continuous - fill, scale_y_continuous() with explicit breaks for mel-band labels — all - idiomatic ggplot2 + comment: geom_tile, scale_fill_gradient with limits/breaks/oob, guide_colorbar + customization, scale_y_continuous with custom breaks/labels, expand=c(0,0). - id: LM-02 name: Distinctive Features score: 3 max: 5 - passed: false - comment: Uses ggplot2 long-format data pipeline effectively; custom Y-axis - break mapping is a nice ggplot2 pattern; does not leverage faceting, coord_trans, - or other distinctive ggplot2 capabilities + passed: true + comment: Full mel filterbank in pure R; custom Hz labels from mel scale inversion; + guide_colorbar barheight/barwidth; annotate() with fontface='italic'. verdict: APPROVED impl_tags: dependencies: - ragg - - scales techniques: - colorbar + - annotations - manual-ticks + - layer-composition patterns: - data-generation - matrix-construction