Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions plots/waveform-audio/implementations/javascript/chartjs.js
Original file line number Diff line number Diff line change
@@ -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 },
},
},
},
});
253 changes: 253 additions & 0 deletions plots/waveform-audio/metadata/javascript/chartjs.yaml
Original file line number Diff line number Diff line change
@@ -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
Loading