From f2ea8187b7b6ac7fb6de5e158e9210be793088aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Jul 2026 07:14:34 +0000 Subject: [PATCH 1/5] feat(highcharts): implement swarm-basic --- .../implementations/javascript/highcharts.js | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 plots/swarm-basic/implementations/javascript/highcharts.js diff --git a/plots/swarm-basic/implementations/javascript/highcharts.js b/plots/swarm-basic/implementations/javascript/highcharts.js new file mode 100644 index 0000000000..c3ff5902c3 --- /dev/null +++ b/plots/swarm-basic/implementations/javascript/highcharts.js @@ -0,0 +1,133 @@ +// anyplot.ai +// swarm-basic: Basic Swarm Plot +// Library: Highcharts 12.6.0 | Node 22 +// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license) +// Quality: pending | Created: 2026-07-26 + +const t = window.ANYPLOT_TOKENS; + +// --- Deterministic PRNG (mulberry32) + Box-Muller normal ------------------- +function mulberry32(seed) { + return function () { + seed |= 0; + seed = (seed + 0x6d2b79f5) | 0; + let x = Math.imul(seed ^ (seed >>> 15), 1 | seed); + x = (x + Math.imul(x ^ (x >>> 7), 61 | x)) ^ x; + return ((x ^ (x >>> 14)) >>> 0) / 4294967296; + }; +} + +const rand = mulberry32(42); +function randNormal(mean, sd) { + const u1 = Math.max(rand(), 1e-9); + const u2 = rand(); + const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2); + return mean + z * sd; +} + +// --- Data: CRP biomarker levels (mg/L) across treatment groups ------------- +const groups = [ + { name: "Placebo", mean: 8.2, sd: 2.4, n: 42 }, + { name: "Low Dose", mean: 6.5, sd: 2.1, n: 40 }, + { name: "Med Dose", mean: 4.8, sd: 1.9, n: 40 }, + { name: "High Dose", mean: 3.1, sd: 1.6, n: 38 }, +]; +const categories = groups.map((g) => g.name); + +groups.forEach((g) => { + g.values = Array.from({ length: g.n }, () => Math.max(0.2, randNormal(g.mean, g.sd))); + g.sampleMean = g.values.reduce((a, b) => a + b, 0) / g.values.length; +}); + +// --- Beeswarm layout: bin points by value, fan them out from center-out ---- +function beeswarmOffsets(values, binWidth, gap) { + const order = values.map((v, i) => i).sort((a, b) => values[a] - values[b]); + const offsets = new Array(values.length).fill(0); + let bin = []; + let binStart = null; + + const flushBin = () => { + bin.forEach((idx, k) => { + const side = k % 2 === 0 ? 1 : -1; + const rank = Math.ceil((k + 1) / 2); + offsets[idx] = side * rank * gap; + }); + bin = []; + }; + + order.forEach((i) => { + const v = values[i]; + if (binStart === null || v - binStart > binWidth) { + flushBin(); + binStart = v; + } + bin.push(i); + }); + flushBin(); + + return offsets; +} + +const BIN_WIDTH = 0.5; // mg/L — points within this range share a swarm bin +const SWARM_GAP = 0.045; // category-axis units between adjacent swarm points + +groups.forEach((g, i) => { + const offsets = beeswarmOffsets(g.values, BIN_WIDTH, SWARM_GAP); + g.points = g.values.map((v, k) => ({ x: i + offsets[k], y: Number(v.toFixed(2)) })); +}); + +// --- Chart ------------------------------------------------------------------- +Highcharts.chart("container", { + chart: { + type: "scatter", + backgroundColor: "transparent", + animation: false, + style: { fontFamily: "inherit" }, + }, + credits: { enabled: false }, + colors: t.palette, + title: { + text: "swarm-basic · javascript · highcharts · anyplot.ai", + style: { color: t.ink, fontSize: "22px", fontWeight: "600" }, + }, + xAxis: { + categories, + min: -0.6, + max: categories.length - 1 + 0.6, + tickPositions: categories.map((_, i) => i), + lineColor: t.inkSoft, + tickColor: t.inkSoft, + gridLineWidth: 0, + labels: { style: { color: t.inkSoft, fontSize: "14px" } }, + title: { text: "Treatment Group", style: { color: t.inkSoft, fontSize: "16px" } }, + }, + yAxis: { + title: { text: "CRP Level (mg/L)", style: { color: t.inkSoft, fontSize: "16px" } }, + gridLineColor: t.grid, + labels: { style: { color: t.inkSoft, fontSize: "14px" } }, + }, + legend: { + itemStyle: { color: t.inkSoft, fontSize: "14px" }, + itemHoverStyle: { color: t.ink }, + }, + plotOptions: { + series: { animation: false }, + scatter: { marker: { radius: 5, lineWidth: 0 }, opacity: 0.85 }, + }, + series: [ + ...groups.map((g, i) => ({ + name: g.name, + color: t.palette[i], + data: g.points, + showInLegend: false, + })), + { + name: "Group mean", + type: "scatter", + color: t.ink, + data: groups.map((g, i) => ({ x: i, y: Number(g.sampleMean.toFixed(2)) })), + marker: { symbol: "diamond", radius: 8, lineColor: t.pageBg, lineWidth: 2 }, + showInLegend: true, + }, + ], +}); From efd06c7104a3c08c296ff391665ca275fef85d02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Jul 2026 07:14:44 +0000 Subject: [PATCH 2/5] chore(highcharts): add metadata for swarm-basic --- .../metadata/javascript/highcharts.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plots/swarm-basic/metadata/javascript/highcharts.yaml diff --git a/plots/swarm-basic/metadata/javascript/highcharts.yaml b/plots/swarm-basic/metadata/javascript/highcharts.yaml new file mode 100644 index 0000000000..163d70597a --- /dev/null +++ b/plots/swarm-basic/metadata/javascript/highcharts.yaml @@ -0,0 +1,21 @@ +# Per-library metadata for highcharts implementation of swarm-basic +# Auto-generated by impl-generate.yml + +library: highcharts +language: javascript +specification_id: swarm-basic +created: '2026-07-26T07:14:44Z' +updated: '2026-07-26T07:14:44Z' +generated_by: claude-sonnet +workflow_run: 30192249117 +issue: 974 +language_version: 22.23.1 +library_version: 12.6.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-dark.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 1384a4fc1bc0fed45c1cd4335e84edeff0b94c58 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Jul 2026 07:19:11 +0000 Subject: [PATCH 3/5] chore(highcharts): update quality score 87 and review feedback for swarm-basic --- .../implementations/javascript/highcharts.js | 5 +- .../metadata/javascript/highcharts.yaml | 255 +++++++++++++++++- 2 files changed, 250 insertions(+), 10 deletions(-) diff --git a/plots/swarm-basic/implementations/javascript/highcharts.js b/plots/swarm-basic/implementations/javascript/highcharts.js index c3ff5902c3..a78d73cf0c 100644 --- a/plots/swarm-basic/implementations/javascript/highcharts.js +++ b/plots/swarm-basic/implementations/javascript/highcharts.js @@ -1,8 +1,7 @@ // anyplot.ai // swarm-basic: Basic Swarm Plot -// Library: Highcharts 12.6.0 | Node 22 -// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license) -// Quality: pending | Created: 2026-07-26 +// Library: highcharts 12.6.0 | JavaScript 22.23.1 +// Quality: 87/100 | Created: 2026-07-26 const t = window.ANYPLOT_TOKENS; diff --git a/plots/swarm-basic/metadata/javascript/highcharts.yaml b/plots/swarm-basic/metadata/javascript/highcharts.yaml index 163d70597a..5fe7f3946d 100644 --- a/plots/swarm-basic/metadata/javascript/highcharts.yaml +++ b/plots/swarm-basic/metadata/javascript/highcharts.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for highcharts implementation of swarm-basic -# Auto-generated by impl-generate.yml - library: highcharts language: javascript specification_id: swarm-basic created: '2026-07-26T07:14:44Z' -updated: '2026-07-26T07:14:44Z' +updated: '2026-07-26T07:19:11Z' generated_by: claude-sonnet workflow_run: 30192249117 issue: 974 @@ -15,7 +12,251 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/swarm-bas preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-dark.html -quality_score: null +quality_score: 87 review: - strengths: [] - weaknesses: [] + strengths: + - 'Excellent CVD-safe redundant encoding: each treatment group uses both a distinct + Imprint palette color AND a distinct marker shape (circle/diamond/square/triangle), + so groups stay distinguishable even without relying on hue.' + - Correct Imprint palette in canonical order (green -> C475FD -> 4467A3 -> BD8233) + identical across both themes; chrome (background, text, grid) flips correctly + light-to-dark with no legibility issues in either render. + - Genuine beeswarm layout algorithm (bin-and-fan-out via beeswarmOffsets) backed + by a deterministic seeded mulberry32 PRNG, producing a real swarm distribution + rather than a scatter/jitter approximation. + - Group mean diamond markers with theme-adaptive fill (t.ink / pageBg border) add + a clear at-a-glance dose-response story, directly fulfilling the spec's suggestion + to add a mean marker per category. + - 'Clean, minimal chrome: x-axis gridlines removed, a single subtle y-axis grid, + unobtrusive axis lines, and no legend clutter (category names live on the x-axis; + only ''Group mean'' appears in the legend).' + weaknesses: + - 'Library Mastery reads as generic: scatter + manual x-offsets is a solid workaround + for the missing native beeswarm series, but nothing showcases a Highcharts-distinctive + capability beyond marker symbols. Consider a library-specific touch (e.g. dataLabels + on the mean markers, or a subtle connector/halo along the swarm axis) to lift + LM-02.' + - Marker opacity/size (radius 5, opacity 0.85) is static across all four groups + even though bin density differs visibly (Placebo and Low Dose are denser near + the median than High Dose) — a touch more transparency or gap in the denser groups + would ease within-bin overlap reading. + - 'Design Excellence is strong but not exceptional: axis lines are still visible + (not fully removed) and the mean-diamond markers have no additional refinement + (e.g. subtle drop-shadow or size hierarchy) that would push the chart toward publication-level + polish.' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1 — not pure white, not dark. + Chrome: Title "swarm-basic · javascript · highcharts · anyplot.ai" in bold dark ink at top center, clearly legible. Y-axis title "CRP Level (mg/L)" and x-axis title "Treatment Group" in medium-gray ink, both readable with good contrast. Tick labels (0-15 on y-axis, four category names on x-axis) are dark and crisp. Subtle horizontal gridlines on the y-axis only; x-axis has no gridlines, keeping the swarm columns clean. + Data: Four treatment groups (Placebo, Low Dose, Med Dose, High Dose) each rendered as a vertical beeswarm of scatter points, fanned out horizontally to avoid overlap. Placebo = green circles (#009E73, the mandated first-series color), Low Dose = purple/violet diamonds, Med Dose = slate-blue squares, High Dose = amber/ochre triangles — matching the Imprint canonical palette order. A black diamond marker per group marks the sample mean, with a legend entry "Group mean" at the bottom. Values decrease from Placebo (mean ~8.0) to High Dose (mean ~3.3), clearly visible as a dose-response trend. + Legibility verdict: PASS — all text (title, axis titles, tick labels, legend) reads clearly against the light background; no light-on-light issues. + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17 — not pure black, not light. + Chrome: Title, axis titles, and tick labels all render in light gray/white and remain clearly legible against the dark background. Gridlines are the same subtle single-axis treatment as the light render, still visible but unobtrusive. The group-mean marker switches from black to white to remain visible on the dark surface (theme-adaptive chrome via t.ink). + Data: Data colors are pixel-identical to the light render — same green circles, purple diamonds, slate-blue squares, and amber triangles, confirming only chrome (not data colors) adapts between themes. The dose-response pattern (decreasing mean CRP across groups) reads identically to the light version. + Legibility verdict: PASS — no dark-on-dark failures; all text and data markers are clearly distinguishable from the near-black background. + 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 22px, axis titles 16px, tick/legend + 14px); well-proportioned in both themes, no overflow. + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No text overlap in either render. + - id: VQ-03 + name: Element Visibility + score: 5 + max: 6 + passed: true + comment: Marker radius 5 with opacity 0.85 is visible and appropriate for + 38-42 points per group; slight within-bin overlap in the densest columns + keeps this from a perfect score. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Color AND marker shape both vary per group — fully CVD-safe redundant + encoding. + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Plot fills the canvas well, balanced margins, legend sits close to + the plot, nothing cut off. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: '''Treatment Group'' and ''CRP Level (mg/L)'' — descriptive with + units.' + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series #009E73, remaining series follow Imprint canonical + order, theme-correct backgrounds/chrome in both renders.' + design_excellence: + score: 13 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Thoughtful color+shape pairing per group and mean markers go beyond + a well-configured default, but stop short of publication-level polish. + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: X-axis gridlines removed, single subtle y-axis grid, minimal legend + — good refinement though axis lines remain visible. + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Group-mean diamonds create a clear visual hierarchy highlighting + the dose-response trend at a glance. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Genuine beeswarm/swarm layout via binned, fanned-out scatter offsets + — correct approach given no native Highcharts beeswarm series. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Consistent point sizes, clear category spacing, color-encodes category, + and adds the suggested group-mean marker. + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Category on x-axis, CRP value on y-axis, all points visible within + axis bounds. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title format exactly matches the mandated pattern; legend correctly + limited to 'Group mean' since categories already label the x-axis. + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 5 + max: 6 + passed: true + comment: Shows differing spreads and a visible outlier (Placebo ~0.3 and ~14) + alongside the dose-response trend. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: CRP biomarker levels across a 4-arm clinical trial — realistic, neutral, + comprehensible scenario. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: CRP values (0.2-14 mg/L) and the decreasing dose-response trend are + physiologically plausible. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Top-down flow (data -> layout -> chart); helper functions are justified + by the beeswarm algorithm itself, not over-engineering. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Seeded mulberry32(42) PRNG — fully deterministic. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: No imports needed or present; pure vanilla JS. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Appropriate complexity for the swarm algorithm, no fake functionality. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Correct mount-node contract, chart + series animation disabled, no + deprecated APIs. + library_mastery: + score: 6 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Idiomatic scatter-with-categories construction is the correct high-level + approach given the core bundle's lack of a native beeswarm series. + - id: LM-02 + name: Distinctive Features + score: 2 + max: 5 + passed: false + comment: Per-group marker symbols and a bordered mean-diamond are nice touches, + but the technique isn't distinctive to Highcharts specifically — could be + replicated easily in other scatter-capable libraries. + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - manual-ticks + patterns: + - data-generation + - iteration-over-groups + dataprep: + - binning + styling: + - alpha-blending + - edge-highlighting From 38599e509a54bd6dc734e773b1d6c5ecf3a25c78 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Jul 2026 07:25:08 +0000 Subject: [PATCH 4/5] fix(highcharts): address review feedback for swarm-basic Attempt 1/4 - fixes based on AI review --- .../implementations/javascript/highcharts.js | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/plots/swarm-basic/implementations/javascript/highcharts.js b/plots/swarm-basic/implementations/javascript/highcharts.js index a78d73cf0c..83e24a29c9 100644 --- a/plots/swarm-basic/implementations/javascript/highcharts.js +++ b/plots/swarm-basic/implementations/javascript/highcharts.js @@ -39,13 +39,17 @@ groups.forEach((g) => { }); // --- Beeswarm layout: bin points by value, fan them out from center-out ---- +// Also reports the densest bin size so denser groups can render more +// transparent, easing within-bin overlap where points stack up the most. function beeswarmOffsets(values, binWidth, gap) { const order = values.map((v, i) => i).sort((a, b) => values[a] - values[b]); const offsets = new Array(values.length).fill(0); let bin = []; let binStart = null; + let maxBinSize = 0; const flushBin = () => { + maxBinSize = Math.max(maxBinSize, bin.length); bin.forEach((idx, k) => { const side = k % 2 === 0 ? 1 : -1; const rank = Math.ceil((k + 1) / 2); @@ -64,15 +68,26 @@ function beeswarmOffsets(values, binWidth, gap) { }); flushBin(); - return offsets; + return { offsets, maxBinSize }; } const BIN_WIDTH = 0.5; // mg/L — points within this range share a swarm bin const SWARM_GAP = 0.045; // category-axis units between adjacent swarm points groups.forEach((g, i) => { - const offsets = beeswarmOffsets(g.values, BIN_WIDTH, SWARM_GAP); + const { offsets, maxBinSize } = beeswarmOffsets(g.values, BIN_WIDTH, SWARM_GAP); g.points = g.values.map((v, k) => ({ x: i + offsets[k], y: Number(v.toFixed(2)) })); + g.maxBinSize = maxBinSize; +}); + +// Denser groups (bigger densest-bin stacks) render more transparent so +// overlapping points stay individually readable; sparser groups stay opaque. +const densestBin = Math.max(...groups.map((g) => g.maxBinSize)); +const sparsestBin = Math.min(...groups.map((g) => g.maxBinSize)); +const binRange = densestBin - sparsestBin || 1; +groups.forEach((g) => { + const density = (g.maxBinSize - sparsestBin) / binRange; + g.opacity = 0.88 - density * 0.28; // 0.88 (sparsest) down to 0.60 (densest) }); // --- Chart ------------------------------------------------------------------- @@ -94,7 +109,7 @@ Highcharts.chart("container", { min: -0.6, max: categories.length - 1 + 0.6, tickPositions: categories.map((_, i) => i), - lineColor: t.inkSoft, + lineWidth: 0, tickColor: t.inkSoft, gridLineWidth: 0, labels: { style: { color: t.inkSoft, fontSize: "14px" } }, @@ -102,6 +117,7 @@ Highcharts.chart("container", { }, yAxis: { title: { text: "CRP Level (mg/L)", style: { color: t.inkSoft, fontSize: "16px" } }, + lineWidth: 0, gridLineColor: t.grid, labels: { style: { color: t.inkSoft, fontSize: "14px" } }, }, @@ -111,21 +127,35 @@ Highcharts.chart("container", { }, plotOptions: { series: { animation: false }, - scatter: { marker: { radius: 5, lineWidth: 0 }, opacity: 0.85 }, + scatter: { marker: { radius: 5, lineWidth: 0 } }, }, series: [ ...groups.map((g, i) => ({ name: g.name, + type: "scatter", color: t.palette[i], data: g.points, + opacity: g.opacity, showInLegend: false, })), { name: "Group mean", type: "scatter", color: t.ink, + zIndex: 5, data: groups.map((g, i) => ({ x: i, y: Number(g.sampleMean.toFixed(2)) })), - marker: { symbol: "diamond", radius: 8, lineColor: t.pageBg, lineWidth: 2 }, + marker: { symbol: "diamond", radius: 9, lineColor: t.pageBg, lineWidth: 3 }, + dataLabels: { + enabled: true, + format: "{point.y:.1f}", + y: -16, + style: { + color: t.ink, + fontSize: "13px", + fontWeight: "600", + textOutline: "2px " + t.pageBg, + }, + }, showInLegend: true, }, ], From b9d74d12bf396da19dc318e9a4e37519c70308cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Jul 2026 07:29:37 +0000 Subject: [PATCH 5/5] chore(highcharts): update quality score 91 and review feedback for swarm-basic --- .../implementations/javascript/highcharts.js | 2 +- .../metadata/javascript/highcharts.yaml | 170 +++++++++--------- 2 files changed, 81 insertions(+), 91 deletions(-) diff --git a/plots/swarm-basic/implementations/javascript/highcharts.js b/plots/swarm-basic/implementations/javascript/highcharts.js index 83e24a29c9..c4bc69164d 100644 --- a/plots/swarm-basic/implementations/javascript/highcharts.js +++ b/plots/swarm-basic/implementations/javascript/highcharts.js @@ -1,7 +1,7 @@ // anyplot.ai // swarm-basic: Basic Swarm Plot // Library: highcharts 12.6.0 | JavaScript 22.23.1 -// Quality: 87/100 | Created: 2026-07-26 +// Quality: 91/100 | Created: 2026-07-26 const t = window.ANYPLOT_TOKENS; diff --git a/plots/swarm-basic/metadata/javascript/highcharts.yaml b/plots/swarm-basic/metadata/javascript/highcharts.yaml index 5fe7f3946d..6b23d10f86 100644 --- a/plots/swarm-basic/metadata/javascript/highcharts.yaml +++ b/plots/swarm-basic/metadata/javascript/highcharts.yaml @@ -2,7 +2,7 @@ library: highcharts language: javascript specification_id: swarm-basic created: '2026-07-26T07:14:44Z' -updated: '2026-07-26T07:19:11Z' +updated: '2026-07-26T07:29:37Z' generated_by: claude-sonnet workflow_run: 30192249117 issue: 974 @@ -12,50 +12,49 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/swarm-bas preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/swarm-basic/javascript/highcharts/plot-dark.html -quality_score: 87 +quality_score: 91 review: strengths: - - 'Excellent CVD-safe redundant encoding: each treatment group uses both a distinct - Imprint palette color AND a distinct marker shape (circle/diamond/square/triangle), - so groups stay distinguishable even without relying on hue.' - - Correct Imprint palette in canonical order (green -> C475FD -> 4467A3 -> BD8233) - identical across both themes; chrome (background, text, grid) flips correctly - light-to-dark with no legibility issues in either render. + - 'Repair directly addressed all three Attempt 1 weaknesses: numeric dataLabels + on the group-mean diamonds now surface the exact dose-response values (8.0 -> + 6.3 -> 4.9 -> 3.3), density-scaled opacity (0.88 sparsest to 0.60 densest) eases + within-bin overlap in the denser groups, and both axis lines are now fully removed + (lineWidth: 0) for cleaner chrome.' - Genuine beeswarm layout algorithm (bin-and-fan-out via beeswarmOffsets) backed - by a deterministic seeded mulberry32 PRNG, producing a real swarm distribution - rather than a scatter/jitter approximation. - - Group mean diamond markers with theme-adaptive fill (t.ink / pageBg border) add - a clear at-a-glance dose-response story, directly fulfilling the spec's suggestion - to add a mean marker per category. - - 'Clean, minimal chrome: x-axis gridlines removed, a single subtle y-axis grid, - unobtrusive axis lines, and no legend clutter (category names live on the x-axis; - only ''Group mean'' appears in the legend).' + by a deterministic seeded mulberry32 PRNG. + - 'Excellent CVD-safe redundant encoding: each treatment group uses both a distinct + Imprint palette color AND a distinct marker shape (circle/diamond/square/triangle).' + - 'Correct Imprint palette in canonical order (#009E73 -> #C475FD -> #4467A3 -> + #BD8233), identical across both themes; chrome flips correctly light-to-dark with + no legibility issues, including the mean-diamond markers and their text-outlined + data labels.' + - 'Clean, minimal chrome: no axis lines, subtle single y-axis grid, unobtrusive + legend limited to the ''Group mean'' entry.' weaknesses: - - 'Library Mastery reads as generic: scatter + manual x-offsets is a solid workaround - for the missing native beeswarm series, but nothing showcases a Highcharts-distinctive - capability beyond marker symbols. Consider a library-specific touch (e.g. dataLabels - on the mean markers, or a subtle connector/halo along the swarm axis) to lift - LM-02.' - - Marker opacity/size (radius 5, opacity 0.85) is static across all four groups - even though bin density differs visibly (Placebo and Low Dose are denser near - the median than High Dose) — a touch more transparency or gap in the denser groups - would ease within-bin overlap reading. - - 'Design Excellence is strong but not exceptional: axis lines are still visible - (not fully removed) and the mean-diamond markers have no additional refinement - (e.g. subtle drop-shadow or size hierarchy) that would push the chart toward publication-level - polish.' + - Opacity is scaled per-group (based on each group's densest bin) rather than per-bin + within a group, so the densest individual bins (e.g. the Med Dose cluster around + 3-4.5 mg/L) still show some marker-on-marker crowding even though the overall + group looks appropriately eased. + - Design Excellence is strong but still short of the 'publication-ready, FiveThirtyEight-level' + tier — the mean-diamond markers and data labels could gain one more layer of refinement + (e.g. a subtle connecting guide or size hierarchy) to fully close the gap to a + top-tier score. + - Library Mastery has improved with the dataLabels flourish but is still a scatter-based + workaround for the missing native beeswarm series; a Highcharts-distinctive technique + (e.g. a jitter/packing plugin pattern or dataLabels connector styling) would push + this further. image_description: |- Light render (plot-light.png): Background: Warm off-white, consistent with #FAF8F1 — not pure white, not dark. - Chrome: Title "swarm-basic · javascript · highcharts · anyplot.ai" in bold dark ink at top center, clearly legible. Y-axis title "CRP Level (mg/L)" and x-axis title "Treatment Group" in medium-gray ink, both readable with good contrast. Tick labels (0-15 on y-axis, four category names on x-axis) are dark and crisp. Subtle horizontal gridlines on the y-axis only; x-axis has no gridlines, keeping the swarm columns clean. - Data: Four treatment groups (Placebo, Low Dose, Med Dose, High Dose) each rendered as a vertical beeswarm of scatter points, fanned out horizontally to avoid overlap. Placebo = green circles (#009E73, the mandated first-series color), Low Dose = purple/violet diamonds, Med Dose = slate-blue squares, High Dose = amber/ochre triangles — matching the Imprint canonical palette order. A black diamond marker per group marks the sample mean, with a legend entry "Group mean" at the bottom. Values decrease from Placebo (mean ~8.0) to High Dose (mean ~3.3), clearly visible as a dose-response trend. - Legibility verdict: PASS — all text (title, axis titles, tick labels, legend) reads clearly against the light background; no light-on-light issues. + Chrome: Bold dark title "swarm-basic · javascript · highcharts · anyplot.ai" centered at top; axis titles "Treatment Group" (x) and "CRP Level (mg/L)" (y) both descriptive with units; tick labels dark/legible; subtle horizontal gridlines only, no visible axis lines. All chrome text clearly readable against the light background. + Data: Four treatment groups (Placebo, Low Dose, Med Dose, High Dose) rendered as beeswarms of scatter points fanned out to avoid overlap — Placebo in green circles (#009E73), Low Dose in light-purple diamonds, Med Dose in slate-blue squares, High Dose in amber triangles, matching the Imprint canonical palette order. A black diamond marks each group's sample mean, labeled with its numeric value (8.0, 6.3, 4.9, 3.3) in bold text with a light outline for legibility against overlapping points — the descending sequence makes the dose-response trend immediately visible. Marker opacity is tuned per group's density (more transparent for denser groups) to ease within-bin overlap. + Legibility verdict: PASS — all text (title, axis titles, ticks, mean-value labels, legend) is clearly readable, no overlap with data points. Dark render (plot-dark.png): Background: Warm near-black, consistent with #1A1A17 — not pure black, not light. - Chrome: Title, axis titles, and tick labels all render in light gray/white and remain clearly legible against the dark background. Gridlines are the same subtle single-axis treatment as the light render, still visible but unobtrusive. The group-mean marker switches from black to white to remain visible on the dark surface (theme-adaptive chrome via t.ink). - Data: Data colors are pixel-identical to the light render — same green circles, purple diamonds, slate-blue squares, and amber triangles, confirming only chrome (not data colors) adapts between themes. The dose-response pattern (decreasing mean CRP across groups) reads identically to the light version. - Legibility verdict: PASS — no dark-on-dark failures; all text and data markers are clearly distinguishable from the near-black background. + Chrome: Same title/axis titles/tick labels now rendered in light gray/white, fully legible against the dark background. Gridlines remain subtle but visible. + Data: Data colors are pixel-identical to the light render (green/purple/blue/amber) — only chrome adapted. The group-mean diamond switches to a cream/white fill so it stays visible against the dark surface, and its numeric data label (e.g. "3.3") uses a dark text-outline against the light fill, remaining crisp. No dark-on-dark failures observed in tick labels, titles, or the mean-marker labels. + Legibility verdict: PASS — no dark-on-dark issues found; all text and data markers remain distinguishable from the near-black background. criteria_checklist: visual_quality: score: 29 @@ -66,75 +65,71 @@ review: score: 8 max: 8 passed: true - comment: All font sizes explicitly set (title 22px, axis titles 16px, tick/legend - 14px); well-proportioned in both themes, no overflow. + comment: All font sizes explicitly set, well-proportioned and readable in + both themes - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: No text overlap in either render. + comment: Mean-value data labels do not collide with markers or other text + in either render - id: VQ-03 name: Element Visibility score: 5 max: 6 passed: true - comment: Marker radius 5 with opacity 0.85 is visible and appropriate for - 38-42 points per group; slight within-bin overlap in the densest columns - keeps this from a perfect score. + comment: Density-scaled opacity eases overlap in denser groups but some marker-on-marker + crowding remains in the densest bins - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Color AND marker shape both vary per group — fully CVD-safe redundant - encoding. + comment: Color AND marker shape both vary per group, fully CVD-safe - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Plot fills the canvas well, balanced margins, legend sits close to - the plot, nothing cut off. + comment: Balanced margins, nothing cut off, canvas gate passed - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: '''Treatment Group'' and ''CRP Level (mg/L)'' — descriptive with - units.' + comment: Descriptive axis titles with units - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series #009E73, remaining series follow Imprint canonical - order, theme-correct backgrounds/chrome in both renders.' + comment: Correct Imprint canonical order, theme-correct chrome in both renders design_excellence: - score: 13 + score: 16 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 5 + score: 6 max: 8 passed: true - comment: Thoughtful color+shape pairing per group and mean markers go beyond - a well-configured default, but stop short of publication-level polish. + comment: Thoughtful color+shape pairing, numeric mean labels, and density-tuned + opacity lift this above a well-configured default - id: DE-02 name: Visual Refinement - score: 4 + score: 5 max: 6 passed: true - comment: X-axis gridlines removed, single subtle y-axis grid, minimal legend - — good refinement though axis lines remain visible. + comment: Axis lines now fully removed, subtle single y-axis grid, minimal + legend - id: DE-03 name: Data Storytelling - score: 4 + score: 5 max: 6 passed: true - comment: Group-mean diamonds create a clear visual hierarchy highlighting - the dose-response trend at a glance. + comment: Numeric mean labels turn the implicit dose-response hierarchy into + an explicit, immediately readable narrative spec_compliance: score: 15 max: 15 @@ -144,29 +139,26 @@ review: score: 5 max: 5 passed: true - comment: Genuine beeswarm/swarm layout via binned, fanned-out scatter offsets - — correct approach given no native Highcharts beeswarm series. + comment: Genuine beeswarm via binned, fanned-out scatter offsets - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Consistent point sizes, clear category spacing, color-encodes category, - and adds the suggested group-mean marker. + comment: Consistent point sizes, clear group spacing, color encoding, group-mean + marker - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Category on x-axis, CRP value on y-axis, all points visible within - axis bounds. + comment: Category on x-axis, value on y-axis, all data visible - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title format exactly matches the mandated pattern; legend correctly - limited to 'Group mean' since categories already label the x-axis. + comment: Title format exactly matches mandate; legend appropriately minimal data_quality: score: 14 max: 15 @@ -176,22 +168,21 @@ review: score: 5 max: 6 passed: true - comment: Shows differing spreads and a visible outlier (Placebo ~0.3 and ~14) - alongside the dose-response trend. + comment: Differing spreads and visible outliers alongside the dose-response + trend - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: CRP biomarker levels across a 4-arm clinical trial — realistic, neutral, - comprehensible scenario. + comment: CRP biomarker levels across a 4-arm clinical trial, realistic and + neutral - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: CRP values (0.2-14 mg/L) and the decreasing dose-response trend are - physiologically plausible. + comment: Physiologically plausible CRP values and trend code_quality: score: 10 max: 10 @@ -201,35 +192,33 @@ review: score: 3 max: 3 passed: true - comment: Top-down flow (data -> layout -> chart); helper functions are justified - by the beeswarm algorithm itself, not over-engineering. + comment: Top-down flow; helper functions justified by the beeswarm algorithm - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Seeded mulberry32(42) PRNG — fully deterministic. + comment: Seeded mulberry32(42) PRNG - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: No imports needed or present; pure vanilla JS. + comment: No imports needed or present - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Appropriate complexity for the swarm algorithm, no fake functionality. + comment: Appropriate complexity, no fake functionality - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Correct mount-node contract, chart + series animation disabled, no - deprecated APIs. + comment: Correct mount-node contract, animation disabled on chart and series library_mastery: - score: 6 + score: 7 max: 10 items: - id: LM-01 @@ -237,21 +226,23 @@ review: score: 4 max: 5 passed: true - comment: Idiomatic scatter-with-categories construction is the correct high-level - approach given the core bundle's lack of a native beeswarm series. + comment: Idiomatic scatter-with-categories construction given the core bundle's + lack of a native beeswarm series - id: LM-02 name: Distinctive Features - score: 2 + score: 3 max: 5 - passed: false - comment: Per-group marker symbols and a bordered mean-diamond are nice touches, - but the technique isn't distinctive to Highcharts specifically — could be - replicated easily in other scatter-capable libraries. - verdict: REJECTED + passed: true + comment: Numeric dataLabels on the mean markers and per-series showInLegend + control are Highcharts-specific touches, though still not a fully distinctive + showcase + verdict: APPROVED impl_tags: dependencies: [] techniques: - manual-ticks + - annotations + - custom-legend patterns: - data-generation - iteration-over-groups @@ -259,4 +250,3 @@ impl_tags: - binning styling: - alpha-blending - - edge-highlighting