<!-- letter "C" made in Adobe Illustrator CS5 -->
<svg version="1.1"
   xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
   x="0px" y="0px" width="202px" height="264px"
   overflow="visible" enable-background="new -0.469 -0.136 202 264" xml:space="preserve">
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M200.5,204.792
  c0,0-35.703,58.341-99.988,58.341C36.223,263.132,0.5,210.862,0.5,131.531C0.5,52.204,38.584,0.5,100.5,0.5
  c61.924,0,85.354,51.704,85.354,51.704"/>
</svg>
import Two from 'https://cdn.skypack.dev/two.js@latest';

addBackdrop(25);

const radius = 40,
      editColor = 'rgb(79, 128, 255)',
      mouse = new Two.Vector(),
      temp = new Two.Vector();

const two = new Two({
  type: Two.Types.canvas,
  fullscreen: true,
  autostart: true
}).appendTo(document.body);

let cx, cy, current = null, isDragging = false;

let svg = document.querySelector('svg');
svg.style.display = 'none';

svg = two.interpret(svg);
svg.center();
svg.linewidth = radius;
svg.cap = svg.join = 'round';
svg.noFill().stroke = '#333';

const points = new Two.Points();
points.size = radius / 4;
points.noStroke().fill = editColor;

for (let i = 0; i < svg.children.length; i++) {

  const child = svg.children[i];
  const vertices = child.vertices;
  
  for (let j = 0; j < vertices.length; j++) {

    const v = child.vertices[j];

    v.relative = false;
    v.controls.left.add(v);
    v.controls.right.add(v);

    if (j === 0 || j === vertices.length - 1) {

      points.vertices.push(v);

    } else {

      points.vertices.push(v, v.controls.left, v.controls.right);
      const vertices = [v.controls.left, v.clone(), v.controls.right];
      const line = new Two.Path(vertices);
      line.noFill().stroke = editColor;
      line.linewidth = 2;

      attach(v, vertices[1]);

      two.add(line);

    }

  }

}

two.add(points);

two.bind('resize', resize);
resize();

window.addEventListener('pointerdown', pointerdown, false);
window.addEventListener('pointermove', pointermove, false);
window.addEventListener('pointerup', pointerup, false);

function resize() {
  cx = two.width * 0.5;
  cy = two.height * 0.5;
  two.scene.position.set(cx, cy);
}

function attach(a, b) {
  a.bind(Two.Events.Types.change, function() {
    b.copy(a);
  });
}

function pointerdown(e) {
  if (current) {
    isDragging = true;
  }
}

function pointermove(e) {

  mouse.x = e.clientX;
  mouse.y = e.clientY;

  if (isDragging) {

    current.x = mouse.x - two.scene.position.x;
    current.y = mouse.y - two.scene.position.y;

  } else {

    let matched = false;

    for (let i = 0; i < points.vertices.length; i++) {

      const v = points.vertices[i];
      const dist = temp.copy(v).add(two.scene.position).distanceToSquared(mouse);

      if (dist < 64) {
        two.renderer.domElement.style.cursor = 'pointer';
        matched = true;
        current = v;
      }

    }

    if (!matched) {
      two.renderer.domElement.style.cursor = 'default';
      current = null;
    }

  }

}

function pointerup(e) {
  isDragging = false;
}

function addBackdrop(d) {

  const dimensions = d || 50;
  const two = new Two({
    type: Two.Types.canvas,
    width: dimensions,
    height: dimensions
  });

  const r = dimensions / 5;
  const center = dimensions / 2;

  const a = two.makeLine(center - r, center, center + r, center);
  const b = two.makeLine(center, center - r, center, center + r);

  a.stroke = b.stroke = '#aaa';
  a.linewidth = b.linewidth = 0.25;

  two.update();

  const style = document.body.style;
  style.backgroundImage = `url(${two.renderer.domElement.toDataURL()})`;
  style.backgroundRepeat = 'repeat';
  style.backgroundSize = `${dimensions}px`;

}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.