diff --git a/plots/waveform-audio/implementations/javascript/chartjs.js b/plots/waveform-audio/implementations/javascript/chartjs.js new file mode 100644 index 0000000000..d92b29c6b9 --- /dev/null +++ b/plots/waveform-audio/implementations/javascript/chartjs.js @@ -0,0 +1,124 @@ +// anyplot.ai +// waveform-audio: Audio Waveform Plot +// Library: chartjs 4.4.7 | JavaScript 22.22.3 +// Quality: 92/100 | Created: 2026-06-03 +//# anyplot-orientation: landscape + +const t = window.ANYPLOT_TOKENS; + +// --- Data (deterministic in-memory) ------------------------------------------ +// Plucked-string model: overlapping harmonics with frequency-proportional decay +const sampleRate = 8820; +const duration = 2.0; +const f0 = 220; // A3 note — 220 cycles over 2 s gives a dense DAW-style view +const totalSamples = sampleRate * duration; +const waveData = new Array(totalSamples); + +for (let i = 0; i < totalSamples; i++) { + const s = i / sampleRate; + const y = + 0.58 * Math.exp(-s * 1.8) * Math.sin(2 * Math.PI * f0 * s) + + 0.25 * Math.exp(-s * 4.0) * Math.sin(2 * Math.PI * f0 * 2 * s) + + 0.12 * Math.exp(-s * 7.5) * Math.sin(2 * Math.PI * f0 * 3 * s) + + 0.05 * Math.exp(-s * 13.0) * Math.sin(2 * Math.PI * f0 * 4 * s); + waveData[i] = { x: s, y }; +} + +const zeroLine = [{ x: 0, y: 0 }, { x: duration, y: 0 }]; + +// --- Mount ------------------------------------------------------------------- +document.getElementById("container").style.backgroundColor = t.pageBg; +const canvas = document.createElement("canvas"); +document.getElementById("container").appendChild(canvas); + +// --- Chart ------------------------------------------------------------------- +const chartTitle = "Plucked String · waveform-audio · javascript · chartjs · anyplot.ai"; + +// Decompose palette[0] (#009E73) into rgba for semi-transparent fill +const hex = t.palette[0]; +const rgb = [ + parseInt(hex.slice(1, 3), 16), + parseInt(hex.slice(3, 5), 16), + parseInt(hex.slice(5, 7), 16), +].join(", "); + +new Chart(canvas, { + type: "line", + data: { + datasets: [ + { + label: "Amplitude", + data: waveData, + parsing: false, + normalized: true, + borderColor: t.palette[0], + backgroundColor: `rgba(${rgb}, 0.3)`, + borderWidth: 1.2, + pointRadius: 0, + fill: "origin", + tension: 0, + }, + { + label: "Zero", + data: zeroLine, + parsing: false, + borderColor: t.inkSoft, + borderDash: [6, 4], + borderWidth: 1, + pointRadius: 0, + fill: false, + tension: 0, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + animation: false, + plugins: { + title: { + display: true, + text: chartTitle, + color: t.ink, + font: { size: 22, weight: "600" }, + padding: { top: 10, bottom: 18 }, + }, + legend: { display: false }, + }, + scales: { + x: { + type: "linear", + min: 0, + max: duration, + title: { + display: true, + text: "Time (s)", + color: t.ink, + font: { size: 18 }, + }, + ticks: { + color: t.inkSoft, + font: { size: 14 }, + callback: (v) => `${v.toFixed(1)}s`, + }, + grid: { color: t.grid }, + }, + y: { + min: -1.0, + max: 1.0, + title: { + display: true, + text: "Amplitude", + color: t.ink, + font: { size: 18 }, + }, + ticks: { + color: t.inkSoft, + font: { size: 14 }, + stepSize: 0.5, + }, + grid: { color: t.grid }, + }, + }, + }, +}); diff --git a/plots/waveform-audio/metadata/javascript/chartjs.yaml b/plots/waveform-audio/metadata/javascript/chartjs.yaml new file mode 100644 index 0000000000..ac452704da --- /dev/null +++ b/plots/waveform-audio/metadata/javascript/chartjs.yaml @@ -0,0 +1,253 @@ +library: chartjs +language: javascript +specification_id: waveform-audio +created: '2026-06-03T01:26:41Z' +updated: '2026-06-03T01:32:59Z' +generated_by: claude-sonnet +workflow_run: 26857869609 +issue: 4563 +language_version: 22.22.3 +library_version: 4.4.7 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/javascript/chartjs/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/javascript/chartjs/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/javascript/chartjs/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/waveform-audio/javascript/chartjs/plot-dark.html +quality_score: 92 +review: + strengths: + - Physically accurate plucked-string model (4 harmonics with frequency-proportional + decay) produces a realistic, story-telling waveform + - 'Correct use of Chart.js performance optimizations: parsing:false + normalized:true + for 17,640 data points' + - Semi-transparent fill:origin creates effective bilateral waveform envelope without + obscuring the signal + - Dashed zero-reference line using inkSoft token is acoustically meaningful and + theme-adaptive + - Legend correctly suppressed for single-series plot; descriptive title prefix 'Plucked + String' adds domain context + - Fully deterministic (pure math, no RNG); all font sizes explicitly set + weaknesses: + - Chart.js default chart-area border (all 4 spines visible) — the style guide calls + for removing top and right spines; add border.display:false to top/right axis + scale config + - Y-axis label says 'Amplitude' — spec and style best practice would be 'Normalized + Amplitude' to match the -1 to +1 range context + - Spec notes min/max envelope rendering for dense waveforms to avoid aliasing artifacts; + implementation renders all 17,640 raw samples without envelope reduction (visually + acceptable at this canvas size, but the technique is absent) + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1. Correct. + Chrome: Title "Plucked String · waveform-audio · javascript · chartjs · anyplot.ai" in dark ink, spanning ~60-65% of plot width at fontsize 22. X-axis title "Time (s)" and Y-axis title "Amplitude" in dark ink at fontsize 18. Tick labels (0.0s–2.0s, -1.0 to 1.0) in secondary ink at fontsize 14. All text clearly readable. + Data: Dense oscillating waveform in #009E73 (brand green) with 0.3-alpha semi-transparent green fill using fill:"origin". Exponential decay from peak amplitude ~0.7 at t=0 to near-silence at t=2.0s. Dashed zero-reference line in inkSoft color. Four-harmonic plucked-string model creates natural-looking decay envelope. + Legibility verdict: PASS — all text readable against warm off-white background, no dark-on-light issues. + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17. Correct. + Chrome: Title in light (#F0EFE8) text, clearly visible. Axis titles "Time (s)" and "Amplitude" in primary light ink. Tick labels in secondary light ink (#B8B7B0). All text remains readable against the dark surface. + Data: Waveform data colors are identical to light render — same #009E73 green line and fill. The 0.3-alpha fill over the dark background produces a dark teal fill that recedes appropriately without losing the shape. No dark-on-dark failures detected. + Legibility verdict: PASS — all text readable against warm near-black background, no dark-on-dark issues. + criteria_checklist: + visual_quality: + score: 30 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 8 + max: 8 + passed: true + comment: All font sizes explicitly set (title:22, axes:18, ticks:14). Well-proportioned + in both themes. Title spans ~60-65% of plot width. + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No text overlaps. Dense waveform data does not collide with labels. + Legend suppressed for single series. + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: 17,640 samples rendered with borderWidth:1.2 and pointRadius:0 — + appropriate for dense audio waveform. Decay envelope clearly visible. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: 'Single series in #009E73 with high contrast on both backgrounds. + Zero line in inkSoft. CVD-safe.' + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Landscape canvas, plot fills ~75% of area. Balanced margins. No content + cut off. Canvas gate passed. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: 'X: ''Time (s)'' with units. Y: ''Amplitude'' descriptive. Title + format correct with descriptive prefix.' + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series is palette[0]=#009E73. Background #FAF8F1 light / #1A1A17 + dark. All chrome theme-adaptive. Data colors identical across themes.' + design_excellence: + score: 14 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: 'Strong design: correct Imprint palette, semi-transparent envelope + fill, descriptive physics-context title. Above defaults but not FiveThirtyEight-level + polish.' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Subtle grid, no legend for single series, clean layout. However Chart.js + default 4-spine border is present (top/right not removed per style guide). + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Exponential decay envelope immediately communicates the plucked string + physics. Clear focal point at t=0 transient. Viewer sees the story without + annotation. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: 'Correct waveform type: dense line with bilateral area fill symmetric + around zero.' + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Filled area (fill:origin), semi-transparent fill, zero reference + line, time x-axis, normalized amplitude y-axis (-1 to +1), synthetic audio + data all present. + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=time in seconds, Y=amplitude. Full range displayed. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: 'Title: ''Plucked String · waveform-audio · javascript · chartjs + · anyplot.ai'' — correct format with descriptive prefix. Legend correctly + suppressed.' + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Four-harmonic model shows transient attack, sustain decay, and silence + — covers all waveform inspection scenarios mentioned in spec. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: A3 plucked string (220 Hz) is a neutral, real-world acoustic scenario. + Not controversial. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 220 Hz A3 note, 8820 Hz sample rate, 2s duration, normalized amplitude + — all acoustically correct. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Linear: tokens → data → mount → chart. No functions or classes.' + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fully deterministic — pure math, no RNG. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: No imports needed (Chart is global). No unused variables. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, well-structured. Hex-to-RGB parsing is concise and purposeful. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Harness handles output. Current Chart.js 4.4.7 API used. + library_mastery: + score: 8 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: 'Expert use: parsing:false + normalized:true for large dataset performance, + fill:origin for symmetric envelope, tension:0 for crisp waveform, pointRadius:0 + for density.' + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: Uses Chart.js-specific parsing:false + normalized:true performance + optimization (unique to Chart.js), fill:origin for bilateral area, and two-dataset + composition for waveform+reference. + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - layer-composition + - manual-ticks + patterns: + - data-generation + dataprep: [] + styling: + - alpha-blending