<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>SVG Path Editor</title>
</head>
<body>
  <div class="edit">
    <div class="edit_row">
      <label>
        <div>Width</div>
        <input id="width" type="number" value="500">
      </label>
      <label>
        <div>Height</div>
        <input id="height" type="number" value="500">
      </label>
      <label>
        <div>Guides</div>
        <input id="guides" type="checkbox" checked>
      </label>
    </div>
    <label>
      <div>Path</div>
      <textarea id="path">M 50,75
Q 150,-30 250,75
T 450,75
C 450,250 450,450 350,450
S 350,250 250,250
l -125,125
h 75
v 75
h -150
v -180
A 100 70 -40 0 1 50,150
Z</textarea>
    </label>
    <label>
      <div>HTML</div>
      <textarea id="html" readonly></textarea>
    </label>
  </div>
  <div class="view" id="view">
    <svg id="svg" xmlns="http://www.w3.org/2000/svg">
      <g id="svg-guides" class="view_guides"></g>
      <path id="svg-path" class="view_path"/>
      <g id="svg-points" class="view_points"></g>
    </svg>
  </div>
</body>
</html>
* {
  box-sizing: border-box;
}
* {
  margin: 0;
}

body {
  font-family: sans-serif;
  display: grid;
  grid-template: "edit view" / 18em calc(100vw - 18em - 1px);
  margin: 0;
  width: 100vw;;
  height: 100vh;
}
.edit {
  grid-area: edit;
  // padding: 1em;
  display: flex;
  flex-direction: column;
  > * {
    + * {
      border-top: 1px solid #ddd;
    }
    &:nth-child(2) {
      flex: 2;
    }
    &:nth-child(3) {
      flex: 1;
    }
  }
  &_row {
    display: flex;
    flex-direction: row;
    > * {
      margin-top: 0;
      flex: 1;
      + * {
        border-left: 1px solid #ddd;
      }
    }
  }
}
.view {
  grid-area: view;
  position: relative;
  background-color: #eee;
  border-left: 1px solid #ddd;
  overflow: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1em;
  svg {
    background-color: #fff;
    overflow: visible;
  }
  &_guides {
    circle {
      fill: currentColor;
      stroke: currentColor;
      stroke-width: 1;
    }
    line, path {
      stroke: currentColor;
      stroke-width: 1;
    }
  }
  &_points {
    circle {
      fill: currentColor;
      stroke: currentColor;   
      stroke-width: 1; 
    }
  }
  &_path {
    fill: transparent;
    stroke: black;
    stroke-width: 2;
  }
}

label {
  display: flex;
  flex-direction: column;
  div {
    padding: 0.5em;
    padding-bottom: 0;
    font-weight: bold;
  }
}
input[type=number], textarea {
  width: 100%;
  flex: 1;
  font: inherit;
  padding: 0.5em;
  border: 0;
  resize: none;
  font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace;
}
input[type=checkbox] {
  font: inherit;
  margin: 0.5em;
}
View Compiled
// arcEllipseCenter credit: https://stackoverflow.com/a/11467200/39428
const arcEllipseCenter = (x1, y1, rx, ry, a, fa, fs, x2, y2) => {
  phi = Snap.rad(a);
  var Cx, Cy;
  var M = $M([[Math.cos(phi), Math.sin(phi)], [-Math.sin(phi), Math.cos(phi)]]);
  var V = $V([(x1 - x2) / 2, (y1 - y2) / 2]);
  var P = M.multiply(V);
  var x1p = P.e(1);
  var y1p = P.e(2);
  rx = Math.abs(rx);
  ry = Math.abs(ry);
  var lambda = x1p * x1p / (rx * rx) + y1p * y1p / (ry * ry);
  if (lambda > 1) {
    rx = Math.sqrt(lambda) * rx;
    ry = Math.sqrt(lambda) * ry;
  }
  var sign = fa == fs ? -1 : 1;
  var co = sign * Math.sqrt((rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p) / (rx * rx * y1p * y1p + ry * ry * x1p * x1p));
  var V = $V([rx * y1p / ry, -ry * x1p / rx]);
  var Cp = V.multiply(co);
  var M = $M([[Math.cos(phi), -Math.sin(phi)], [Math.sin(phi), Math.cos(phi)]]);
  var V = $V([(x1 + x2) / 2, (y1 + y2) / 2]);
  var C = M.multiply(Cp).add(V);
  Cx = C.e(1);
  Cy = C.e(2);
  return [Cx, Cy];
};
//

const widthEl = document.getElementById('width');
const heightEl = document.getElementById('height');
const guidesEl = document.getElementById('guides');
const pathEl = document.getElementById('path');
const svgEl = document.getElementById('svg');
const htmlEl = document.getElementById('html');
const svgPathEl = document.getElementById('svg-path');
const svgPointsEl = document.getElementById('svg-points');
const svgGuidesEl = document.getElementById('svg-guides');
const svgNS = 'http://www.w3.org/2000/svg';

const pointCircle = (x, y, r, a) => {
  return [
    x + r * Snap.cos(a),
    y + r * Snap.sin(a),
  ];
}

const draw = () => {
  const width = widthEl.value;
  const height = heightEl.value;;
  const path = pathEl.value;
  const viewbox = `0 0 ${widthEl.value} ${heightEl.value}`;
  svgPointsEl.innerHTML = '';
  svgGuidesEl.innerHTML = '';
  const pathAr = Snap.parsePathString(path);
  const pathArAbs = Snap.path.toAbsolute(path);
  svgEl.setAttribute('width', width);
  svgEl.setAttribute('height', height);
  svgEl.setAttribute('viewbox', viewbox);
  svgPathEl.setAttribute('d', pathAr.map(v => v.join(' ')).join(' '));
  htmlEl.value = `<svg viewbox="${viewbox}" xmlns="http://www.w3.org/2000/svg"><path d="${pathAr.map(v => v.join(' ')).join(' ')}"></svg>`;
  if (!guidesEl.checked) {
    return;
  }
  let p  = [0, 0];
  let pf = [0, 0];
  let pl = [0, 0];
  let vl = [0, 0];
  pathArAbs.forEach(([c, ...v], i) => {
    let guides = [];
    switch (c) {
      case 'M':
      case 'L':
        p = [v[0], v[1]];
        break;
      case 'H':
        p = [v[0], pl[1]];
        break;
      case 'V':
        p = [pl[0], v[0]];
        break;
      case 'C':
        p = [v[4], v[5]];
        drawGuideHandle(...pl, v[0], v[1], 'dodgerblue');
        drawGuideHandle(...p , v[2], v[3], 'dodgerblue');
        break;
      case 'S':
        p = [v[2], v[3]];
        drawGuideHandle(...p,  v[0], v[1], 'dodgerblue');
        drawGuideHandle(...pl, 
          pl[0] + (pl[0] - vl[2]),
          pl[1] + (pl[1] - vl[3]),
        '#bbb');
        break;
      case 'Q':
        p = [v[2], v[3]];
        drawGuideHandle(...pl, v[0], v[1], 'dodgerblue');
        drawGuideHandle(...p,  v[0], v[1], 'dodgerblue');
        break;
      case 'T':
        p = [v[0], v[1]];
        drawGuideHandle(...pl, 
          pl[0] + (pl[0] - vl[0]),
          pl[1] + (pl[1] - vl[1]),
        '#bbb');
        drawGuideHandle(...p, 
          pl[0] + (pl[0] - vl[0]),
          pl[1] + (pl[1] - vl[1]),
        '#bbb');
        break;
      case 'A':
        p = [v[5], v[6]];
        drawGuideEllipse(...pl, v[0], v[1], v[2], v[3], v[4], v[5], v[6], 'dodgerblue');
        break;
      case 'Z':
        p = pf;
        break;
    }
    drawPoint(...p, 'crimson');
    pl = p;
    vl = v;
    if (i === 0) {
      pf = p;
    }
  })
};
const drawPoint = (x, y, color) => {
  const el = document.createElementNS(svgNS, 'circle');
  el.setAttribute('cx', x);
  el.setAttribute('cy', y);
  el.setAttribute('r', 4);
  el.setAttribute('style', `color: ${color}`);
  svgPointsEl.appendChild(el);
};
const drawGuideDot = (x, y, color) => {
  const el = document.createElementNS(svgNS, 'circle');
  el.setAttribute('cx', x);
  el.setAttribute('cy', y);
  el.setAttribute('r', 4);
  el.setAttribute('style', `color: ${color}`);
  svgGuidesEl.appendChild(el); 
};
const drawGuideLine = (x1, y1, x2, y2, color) => {
  const el = document.createElementNS(svgNS, 'line');
  el.setAttribute('x1', x1);
  el.setAttribute('y1', y1);
  el.setAttribute('x2', x2);
  el.setAttribute('y2', y2);
  el.setAttribute('style', `color: ${color}`);
  svgGuidesEl.appendChild(el); 
}
const drawGuideHandle = (x1, y1, x2, y2, color) => {
  drawGuideLine(x1, y1, x2, y2, color);
  drawGuideDot(x2, y2, color);
}
const drawGuideArc = (x1, y1, rx, ry, a, fa, fs, x2, y2, color) => {
  const el = document.createElementNS(svgNS, 'path');
  el.setAttribute('d', `M ${x1} ${y1} A ${rx} ${ry} ${a} ${fa} ${fs} ${x2} ${y2}`);
  el.setAttribute('fill', 'transparent');
  el.setAttribute('style', `color: ${color}`);
  svgGuidesEl.appendChild(el); 
};
const drawGuideEllipse = (x1, y1, rx, ry, a, fa, fs, x2, y2, color) => {
  drawGuideArc(x1, y1, rx, ry, a, fa ? 0 : 1, fs ? 0 : 1, x2, y2, color);
  const c = arcEllipseCenter(x1, y1, rx, ry, a, fa, fs, x2, y2);
  drawGuideLine(
    ...pointCircle(...c, -rx, a),
    ...pointCircle(...c, rx, a),
  color);
  drawGuideLine(
    ...pointCircle(...c, -ry, a + 90),
    ...pointCircle(...c, ry, a + 90),
  color);
};

draw();
const drawDebounce = _.debounce(draw, 250);
document.querySelectorAll('input[type=number], textarea').forEach(el => el.addEventListener('input', drawDebounce));
document.querySelectorAll('input[type=checkbox]').forEach(el => el.addEventListener('input', draw));

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/sylvester/0.1.3/sylvester.min.js