Raphael 11,264 made vector graphics work in old Internet Explorer by drawing VML there and SVG elsewhere; Dmitry Baranovskiy, its creator, later wrote the SVG-only Snap.svg 14,009 under Adobe's GitHub 29 organization. Both keep thousands of stars and tutorials, but both have stopped, and nobody is fixing their bugs or security reports:
| Old library | Last release | Last commit | Modern replacement |
|---|---|---|---|
| Raphael | 2.3.0, August 2019 | June 2020 | Plain SVG DOM, or Svg.js 11,827 |
| Snap.svg | 0.5.1, February 2017 | March 2022 | Svg.js to build, GSAP 88,281 or anime.js 224,114 to animate |
Porting is direct: paper.circle(x, y, r).attr({ fill }) becomes draw.circle(2 * r).center(x, y).fill(fill) in Svg.js, and .animate() calls become tweens. D3.js 18,089 (7.9.0, ISC license) is the other big SVG library, but it binds data to elements rather than drawing shapes; D3.js is devoted to it.
<!doctype html>
<style>
body { margin: 0; font: 13px system-ui, sans-serif; background: #fafaf7; }
svg { width: 100%; max-width: 600px; display: block; }
text { fill: #333; }
</style>
<svg id="chart" viewBox="0 0 600 300"></svg>
<script>
// A tiny helper instead of Raphael's paper.rect(...).attr({...})
const NS = 'http://www.w3.org/2000/svg';
const svg = document.getElementById('chart');
function el(name, attrs, parent = svg) {
const node = document.createElementNS(NS, name);
for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);
parent.appendChild(node);
return node;
}
const libs = [
{ name: 'Raphael', first: 2008.6, release: 2019.6, commit: 2020.45, color: '#e07a5f', note: '2.3.0 · dormant' },
{ name: 'Snap.svg', first: 2013.8, release: 2017.1, commit: 2022.2, color: '#f2a65a', note: '0.5.1 · dormant' },
{ name: 'D3.js', first: 2011.1, release: 2025.0, commit: 2026.7, color: '#3d5a80', note: '7.9.0 · active' }
];
const x = year => 110 + (year - 2008) * 25; // 2008 → 110px, 2026 → 560px
// Year axis
for (let y = 2008; y <= 2026; y += 2) {
el('line', { x1: x(y), y1: 40, x2: x(y), y2: 240, stroke: '#e3dfd4' });
el('text', { x: x(y), y: 258, 'text-anchor': 'middle', 'font-size': 11 }).textContent = y;
}
el('text', { x: 20, y: 24, 'font-weight': 700, 'font-size': 15 }).textContent = 'Last release vs. last commit';
libs.forEach((lib, i) => {
const cy = 75 + i * 65;
el('text', { x: 12, y: cy + 4, 'font-weight': 600 }).textContent = lib.name;
// life span bar, growing in with the Web Animations API (GSAP or anime.js would tween this)
const bar = el('rect', { x: x(lib.first), y: cy - 9, width: x(lib.commit) - x(lib.first), height: 18, rx: 9, fill: lib.color, opacity: 0.35 });
bar.style.transformOrigin = `${x(lib.first)}px 0`;
bar.animate([{ transform: 'scaleX(0)' }, { transform: 'scaleX(1)' }],
{ duration: 900, delay: i * 200, easing: 'ease-out', fill: 'backwards' });
el('circle', { cx: x(lib.release), cy, r: 7, fill: lib.color });
el('rect', { x: x(lib.commit) - 5, y: cy - 5, width: 10, height: 10, fill: '#fff', stroke: lib.color, 'stroke-width': 2.5 });
el('text', { x: x(lib.first), y: cy - 14, 'font-size': 11 }).textContent = lib.note;
});
// Legend
el('circle', { cx: 120, cy: 282, r: 6, fill: '#666' });
el('text', { x: 132, y: 286, 'font-size': 11 }).textContent = 'last release';
el('rect', { x: 215, y: 277, width: 10, height: 10, fill: '#fff', stroke: '#666', 'stroke-width': 2.5 });
el('text', { x: 232, y: 286, 'font-size': 11 }).textContent = 'last commit';
el('text', { x: 320, y: 286, 'font-size': 11 }).textContent = 'Replace: plain DOM, Svg.js, GSAP, anime.js';
</script>