Snap.svg and Raphaël

Snap.svg and Raphael's Dormancy, and a Pointer to D3.js

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:

Dormant SVG libraries and what replaces them
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.

A release timeline of Raphael, Snap.svg and D3, drawn with the plain SVG DOM that replaces themHTMLLive
<!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>