Spaces:
Running
Running
Draw the medallion as roots and show the full model-size range
Browse filesReplace leaf shapes and time rings with tapered, gently irregular branches using the existing family hierarchy. Retain all 102 family tips and corpus counts, with ancestry highlighting on hover. Spacing is illustrative and the age scale is removed. Update the Overview heading to 318M to 3.2B parameters. Verified family data, hover behaviour, motion preferences, light/dark layouts and prior removals.
- README.md +5 -2
- assets/hero.js +51 -22
- assets/site.css +4 -7
- index.html +1 -1
README.md
CHANGED
|
@@ -39,8 +39,11 @@ Static landing page for the Botanic1 models (Living Models), linking to the mode
|
|
| 39 |
Living Models' palette (pale sage paper, near-black ink, the neon lime as a fill,
|
| 40 |
pill buttons) with two devices of the Space's own: the wordmark set as a row of
|
| 41 |
token cells whose last cell is masked and then predicted (`assets/hero.js`), and the
|
| 42 |
-
corpus's
|
| 43 |
-
(`data/tree.json`, built from `figures/data/plants_family_timetree5.nwk`).
|
|
|
|
|
|
|
|
|
|
| 44 |
headings and controls, Spectral for reading, IBM Plex Mono for tokens, labels and
|
| 45 |
numbers. Detailed charts remain on the benchmark and corpus pages. The overview
|
| 46 |
states a few results from the report and links to their evaluations. The original
|
|
|
|
| 39 |
Living Models' palette (pale sage paper, near-black ink, the neon lime as a fill,
|
| 40 |
pill buttons) with two devices of the Space's own: the wordmark set as a row of
|
| 41 |
token cells whose last cell is masked and then predicted (`assets/hero.js`), and the
|
| 42 |
+
corpus's family tree drawn as spreading roots in the landing-page medallion
|
| 43 |
+
(`data/tree.json`, built from `figures/data/plants_family_timetree5.nwk`). The drawing
|
| 44 |
+
retains the family relationships, with tapered branches and an uneven perimeter;
|
| 45 |
+
its spacing is illustrative. Hovering over a tip shows its family and corpus counts.
|
| 46 |
+
Manrope for
|
| 47 |
headings and controls, Spectral for reading, IBM Plex Mono for tokens, labels and
|
| 48 |
numbers. Detailed charts remain on the benchmark and corpus pages. The overview
|
| 49 |
states a few results from the report and links to their evaluations. The original
|
assets/hero.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
/* Hero: the corpus's
|
| 2 |
and the masked token in every wordmark. Needs d3 when a medallion is present. */
|
| 3 |
(function () {
|
| 4 |
// Private Hugging Face Spaces authorise requests through the __sign token of the
|
|
@@ -57,10 +57,46 @@
|
|
| 57 |
const spans = d.children.map(k => d3.extent(k.leaves(), l => l.x)).sort((p, q) => p[0] - q[0]);
|
| 58 |
d.x = spans.length === 1 ? d.children[0].x : (spans[0][1] + spans[1][0]) / 2;
|
| 59 |
});
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
const
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
const svg = d3.create('svg').attr('viewBox', `0 0 ${size} ${size}`).attr('aria-hidden', 'true');
|
| 66 |
// Rotation is driven from JavaScript on a nested group: a CSS transform would
|
|
@@ -68,24 +104,17 @@
|
|
| 68 |
const centre = svg.append('g').attr('transform', `translate(${c},${c})`);
|
| 69 |
const g = centre.append('g').attr('class', 'spin');
|
| 70 |
if (!reduced) { const period = 480000; let t0 = null; const tick = (ts) => { if (t0 === null) t0 = ts; g.attr('transform', `rotate(${((ts - t0) / period) * 360})`); requestAnimationFrame(tick); }; requestAnimationFrame(tick); }
|
| 71 |
-
const
|
| 72 |
-
|
| 73 |
-
g.selectAll('
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
tips.append('path').attr('class', 'blade').attr('d', d => leafPath(lScale(d.data.windows)));
|
| 81 |
-
tips.append('path').attr('class', 'rib').attr('d', d => `M1,0 L${lScale(d.data.windows) * 0.8},0`);
|
| 82 |
-
tips.append('circle').attr('class', 'hit').attr('r', d => Math.max(6, lScale(d.data.windows) * 0.6)).attr('cx', d => lScale(d.data.windows) * 0.5);
|
| 83 |
-
tips.on('mousemove', (ev, d) => showTip(`<b>${d.data.name}</b> · ${d.data.order}<br>${d.data.n_species} species · ${d.data.windows.toLocaleString()} windows`, ev))
|
| 84 |
-
.on('mouseleave', hideTip);
|
| 85 |
const lab = svg.append('g').attr('transform', `translate(${c},${c})`);
|
| 86 |
-
lab.
|
| 87 |
-
.attr('x', 4).attr('y', d => -((maxAge - d) / maxAge) * R - 4).text(d => `${d} Ma`);
|
| 88 |
-
lab.append('text').attr('class', 'ring-label').attr('x', 4).attr('y', -R - 6).text(`${Math.round(maxAge)} Ma · 102 families · 320 species`);
|
| 89 |
el.appendChild(svg.node());
|
| 90 |
}).catch(() => {});
|
| 91 |
})();
|
|
|
|
| 1 |
+
/* Hero: the corpus's family tree drawn as spreading roots (the medallion),
|
| 2 |
and the masked token in every wordmark. Needs d3 when a medallion is present. */
|
| 3 |
(function () {
|
| 4 |
// Private Hugging Face Spaces authorise requests through the __sign token of the
|
|
|
|
| 57 |
const spans = d.children.map(k => d3.extent(k.leaves(), l => l.x)).sort((p, q) => p[0] - q[0]);
|
| 58 |
d.x = spans.length === 1 ? d.children[0].x : (spans[0][1] + spans[1][0]) / 2;
|
| 59 |
});
|
| 60 |
+
// Keep the family relationships and ordering, with an uneven outer silhouette.
|
| 61 |
+
// This is an illustration of the tree, so no time rings or age scale are shown.
|
| 62 |
+
const seed = name => Array.from(name).reduce((value, ch) => (value * 31 + ch.charCodeAt(0)) >>> 0, 17);
|
| 63 |
+
root.eachBefore(d => {
|
| 64 |
+
const reach = R - 12 + 12 * Math.sin(d.x * 3 + .6) + 7 * Math.sin(d.x * 7 - .8);
|
| 65 |
+
const tipOffset = d.children ? 0 : (seed(d.data.name) % 25) - 12;
|
| 66 |
+
d.y = Math.max(d.parent ? d.parent.y : 0, Math.pow(d.age / maxAge, 1.45) * reach + tipOffset);
|
| 67 |
+
d.width = 1 + 7 * Math.pow(d.data._c / nLeaves, .65);
|
| 68 |
+
});
|
| 69 |
+
const polar = (angle, radius) => [Math.sin(angle) * radius, -Math.cos(angle) * radius];
|
| 70 |
+
const rootPath = ({ source, target }) => {
|
| 71 |
+
const middle = (source.y + target.y) / 2;
|
| 72 |
+
const controls = [polar(source.x, source.y), polar(source.x, middle), polar(target.x, middle), polar(target.x, target.y)];
|
| 73 |
+
const distance = controls.slice(1).reduce((sum, point, i) => sum + Math.hypot(point[0] - controls[i][0], point[1] - controls[i][1]), 0);
|
| 74 |
+
const phase = (seed(target.data.name || String(target.x)) % 628) / 100;
|
| 75 |
+
const amplitude = Math.min(3.5, distance * .025);
|
| 76 |
+
const steps = Math.max(12, Math.ceil(distance / 5));
|
| 77 |
+
const points = d3.range(steps + 1).map(i => {
|
| 78 |
+
const t = i / steps, u = 1 - t;
|
| 79 |
+
const weights = [u ** 3, 3 * u * u * t, 3 * u * t * t, t ** 3];
|
| 80 |
+
const point = [0, 1].map(axis => controls.reduce((sum, p, j) => sum + weights[j] * p[axis], 0));
|
| 81 |
+
const tangent = [0, 1].map(axis => 3 * u * u * (controls[1][axis] - controls[0][axis]) + 6 * u * t * (controls[2][axis] - controls[1][axis]) + 3 * t * t * (controls[3][axis] - controls[2][axis]));
|
| 82 |
+
const length = Math.hypot(...tangent) || 1;
|
| 83 |
+
// The bend fades at each end so adjoining branches meet smoothly.
|
| 84 |
+
const bend = amplitude * Math.sin(Math.PI * t) ** 2 * Math.sin(2 * Math.PI * t + phase);
|
| 85 |
+
return [point[0] - tangent[1] / length * bend, point[1] + tangent[0] / length * bend];
|
| 86 |
+
});
|
| 87 |
+
const sides = [[], []];
|
| 88 |
+
const startWidth = Math.min(source.width, target.width * 1.65);
|
| 89 |
+
const endWidth = target.children ? target.width : .08;
|
| 90 |
+
points.forEach((point, i) => {
|
| 91 |
+
const t = i / steps;
|
| 92 |
+
const before = points[Math.max(0, i - 1)], after = points[Math.min(steps, i + 1)];
|
| 93 |
+
const dx = after[0] - before[0], dy = after[1] - before[1], length = Math.hypot(dx, dy) || 1;
|
| 94 |
+
const width = (startWidth * (1 - t) + endWidth * t) * (1 + .06 * Math.sin(3 * Math.PI * t + phase) * Math.sin(Math.PI * t)) / 2;
|
| 95 |
+
sides[0].push([point[0] - dy / length * width, point[1] + dx / length * width]);
|
| 96 |
+
sides[1].push([point[0] + dy / length * width, point[1] - dx / length * width]);
|
| 97 |
+
});
|
| 98 |
+
return d3.line()(sides[0].concat(sides[1].reverse())) + 'Z';
|
| 99 |
+
};
|
| 100 |
|
| 101 |
const svg = d3.create('svg').attr('viewBox', `0 0 ${size} ${size}`).attr('aria-hidden', 'true');
|
| 102 |
// Rotation is driven from JavaScript on a nested group: a CSS transform would
|
|
|
|
| 104 |
const centre = svg.append('g').attr('transform', `translate(${c},${c})`);
|
| 105 |
const g = centre.append('g').attr('class', 'spin');
|
| 106 |
if (!reduced) { const period = 480000; let t0 = null; const tick = (ts) => { if (t0 === null) t0 = ts; g.attr('transform', `rotate(${((ts - t0) / period) * 360})`); requestAnimationFrame(tick); }; requestAnimationFrame(tick); }
|
| 107 |
+
const branches = g.selectAll('path.root-branch').data(root.links()).join('path').attr('class', 'root-branch').attr('d', rootPath);
|
| 108 |
+
// Each fine tip is still one family. Invisible targets retain the data tooltip.
|
| 109 |
+
const tips = g.selectAll('circle.root-tip').data(root.leaves()).join('circle').attr('class', 'root-tip')
|
| 110 |
+
.attr('cx', d => polar(d.x, d.y)[0]).attr('cy', d => polar(d.x, d.y)[1]).attr('r', 7);
|
| 111 |
+
tips.on('mousemove', (ev, d) => {
|
| 112 |
+
const ancestors = new Set(d.ancestors());
|
| 113 |
+
branches.classed('highlighted', link => ancestors.has(link.target));
|
| 114 |
+
showTip(`<b>${d.data.name}</b> · ${d.data.order}<br>${d.data.n_species} species · ${d.data.windows.toLocaleString()} windows`, ev);
|
| 115 |
+
}).on('mouseleave', () => { branches.classed('highlighted', false); hideTip(); });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
const lab = svg.append('g').attr('transform', `translate(${c},${c})`);
|
| 117 |
+
lab.append('text').attr('class', 'tree-label').attr('x', 0).attr('y', -R - 16).attr('text-anchor', 'middle').text(`${nLeaves} families · ${data.tree.n_species} species`);
|
|
|
|
|
|
|
| 118 |
el.appendChild(svg.node());
|
| 119 |
}).catch(() => {});
|
| 120 |
})();
|
assets/site.css
CHANGED
|
@@ -122,13 +122,10 @@ em, i { font-style: italic; }
|
|
| 122 |
.medallion { position: relative; width: min(780px, 94vw); aspect-ratio: 1; display: grid; place-items: center; }
|
| 123 |
.medallion .hero-art { position: absolute; inset: 0; }
|
| 124 |
.medallion .hero-art svg { width: 100%; height: 100%; display: block; overflow: visible; }
|
| 125 |
-
.hero-art .
|
| 126 |
-
.hero-art .
|
| 127 |
-
.hero-art .
|
| 128 |
-
.hero-art .
|
| 129 |
-
.hero-art .leaf:hover .blade { fill: var(--lime); stroke: var(--ink); }
|
| 130 |
-
.hero-art .ring { fill: none; stroke: var(--rule); stroke-dasharray: 2 5; }
|
| 131 |
-
.hero-art .ring-label { font-family: var(--mono); font-size: 10.5px; fill: var(--ink-3); letter-spacing: 0.08em; }
|
| 132 |
.medallion .centre { position: relative; z-index: 2; text-align: center; padding: 44px 40px 40px; border-radius: 50%; background: radial-gradient(closest-side, var(--sage) 58%, color-mix(in srgb, var(--sage) 70%, transparent) 80%, transparent 100%); pointer-events: none; max-width: 100%; }
|
| 133 |
.medallion .centre > * { pointer-events: auto; }
|
| 134 |
.medallion .org { font-family: var(--sans); font-weight: 600; font-size: 13px; color: var(--ink-3); margin: 0; }
|
|
|
|
| 122 |
.medallion { position: relative; width: min(780px, 94vw); aspect-ratio: 1; display: grid; place-items: center; }
|
| 123 |
.medallion .hero-art { position: absolute; inset: 0; }
|
| 124 |
.medallion .hero-art svg { width: 100%; height: 100%; display: block; overflow: visible; }
|
| 125 |
+
.hero-art .root-branch { fill: var(--tree-2); opacity: .65; transition: fill .2s ease, opacity .2s ease; }
|
| 126 |
+
.hero-art .root-branch.highlighted { fill: var(--accent); opacity: 1; }
|
| 127 |
+
.hero-art .root-tip { fill: transparent; cursor: default; }
|
| 128 |
+
.hero-art .tree-label { font-family: var(--mono); font-size: 10.5px; fill: var(--ink-3); letter-spacing: 0.08em; }
|
|
|
|
|
|
|
|
|
|
| 129 |
.medallion .centre { position: relative; z-index: 2; text-align: center; padding: 44px 40px 40px; border-radius: 50%; background: radial-gradient(closest-side, var(--sage) 58%, color-mix(in srgb, var(--sage) 70%, transparent) 80%, transparent 100%); pointer-events: none; max-width: 100%; }
|
| 130 |
.medallion .centre > * { pointer-events: auto; }
|
| 131 |
.medallion .org { font-family: var(--sans); font-weight: 600; font-size: 13px; color: var(--ink-3); margin: 0; }
|
index.html
CHANGED
|
@@ -47,7 +47,7 @@
|
|
| 47 |
<div class="facts">
|
| 48 |
<article class="fact">
|
| 49 |
<p class="fact-label">Model size</p>
|
| 50 |
-
<h2>318M parameters</h2>
|
| 51 |
<p>Botanic1-S scores 0.758 on the aggregate benchmark across nine task families. PlantCAD2-L scores 0.756, Carbon-8B 0.727, and Evo 2-7B 0.695.</p>
|
| 52 |
<p class="fact-note">Frozen models · aggregate test score (S<sub>bal</sub><sup>test</sup>)</p>
|
| 53 |
<a class="text-link" href="leaderboard.html">Scores by task and species <span aria-hidden="true">↗</span></a>
|
|
|
|
| 47 |
<div class="facts">
|
| 48 |
<article class="fact">
|
| 49 |
<p class="fact-label">Model size</p>
|
| 50 |
+
<h2>318M to 3.2B parameters</h2>
|
| 51 |
<p>Botanic1-S scores 0.758 on the aggregate benchmark across nine task families. PlantCAD2-L scores 0.756, Carbon-8B 0.727, and Evo 2-7B 0.695.</p>
|
| 52 |
<p class="fact-note">Frozen models · aggregate test score (S<sub>bal</sub><sup>test</sup>)</p>
|
| 53 |
<a class="text-link" href="leaderboard.html">Scores by task and species <span aria-hidden="true">↗</span></a>
|