<title>Cubic bezier curve length</title>
<svg id="figure1" width="400" height="400">
<marker id="arrowhead" viewBox="0 0 10 10" refX="40" refY="5"
markerWidth="10" markerHeight="10" orient="auto">
<path d="M10 5 0 10 0 8.7 6.8 5.5 0 5.5 0 4.5 6.8 4.5 0 1.3 0 0Z"/>
<path d="M120 160 35 200 220 260 220 40" stroke="black" fill="none" id="controlPath"/>
<path d="M120 160C35 200 220 260 220 40" stroke="green" fill="none" id="curve"
marker-end="url(#arrowhead)"/>
<circle cx="120" cy="160" r="8" id="p1" class="control-point draggable"/>
<circle cx="35" cy="200" r="8" id="p2" class="control-point draggable"/>
<circle cx="220" cy="260" r="8" id="p3" class="control-point draggable"/>
<circle cx="220" cy="40" r="30" id="p4" class="control-point draggable"/>
<circle cx="-10" cy="-10" r="8" id="splitPoint" class="split-point"/>
<text x="20" y="20" id="curveLength">curve length: 0</text>
<text x="120" y="160" dx="10" id="t1">p1: 120/160</text>
<text x="35" y="200" dx="10" id="t2">p2: 35/200</text>
<text x="220" y="260" dx="10" id="t3">p3: 220/260</text>
<text x="220" y="40" dx="10" id="t4">p4: 220/40</text>
<script src="bezier-curve.js"></script>
// This is a port to JavaScript from [Arc length | A Primer on Bézier Curves]( https://pomax.github.io/bezierinfo/#arclength )
var controlPath = document.getElementById('controlPath');
var curve = document.getElementById('curve');
var p1 = document.getElementById('p1');
var p2 = document.getElementById('p2');
var p3 = document.getElementById('p3');
var p4 = document.getElementById('p4');
var svg = document.getElementById('figure1');
svg.addEventListener('mousemove', onMouseMove);
svg.addEventListener('mouseup', onMouseUp);
var elements = [p1, p2, p3, p4];
for (i = 0; i < elements.length; i++) {
elements[i].addEventListener('mousedown', onMouseDown);
var mouseOffsetX, mouseOffsetY;
function getClientPointInSVG(ev) {
p = svg.createSVGPoint();
m = dragElem.getScreenCTM();
return p.matrixTransform(m.inverse());
function onMouseDown(ev) {
p = getClientPointInSVG(ev);
mouseOffsetX = p.x - dragElem.getAttribute('cx');
mouseOffsetY = p.y - dragElem.getAttribute('cy');
function onMouseMove(ev) {
var p, x, y, circleID, circleTextID, circleTextElem;
p = getClientPointInSVG(ev);
dragElem.setAttribute('cx', x);
dragElem.setAttribute('cy', y);
controlPath.setAttribute('d',
'M' + p1.getAttribute('cx') + ' ' + p1.getAttribute('cy') +
' ' + p2.getAttribute('cx') + ' ' + p2.getAttribute('cy') +
' ' + p3.getAttribute('cx') + ' ' + p3.getAttribute('cy') +
' ' + p4.getAttribute('cx') + ' ' + p4.getAttribute('cy'));
'M' + p1.getAttribute('cx') + ' ' + p1.getAttribute('cy') +
'C' + p2.getAttribute('cx') + ' ' + p2.getAttribute('cy') +
' ' + p3.getAttribute('cx') + ' ' + p3.getAttribute('cy') +
' ' + p4.getAttribute('cx') + ' ' + p4.getAttribute('cy'));
circleID = dragElem.getAttribute('id');
circleTextID = circleID.replace(/^p/, 't');
circleTextElem = document.getElementById(circleTextID);
circleTextElem.setAttribute('x', x);
circleTextElem.setAttribute('y', y);
circleTextElem.firstChild.data = circleID + ': ' + x + '/' + y;
function updateCurveLengthText() {
var textElem = document.getElementById('curveLength');
var curve = new BezierCurve(elements.map(function(elem) {
return {x: +elem.getAttribute('cx'), y: +elem.getAttribute('cy')};
var arcLength = curve.getArcLength();
textElem.firstChild.data = 'curve length: ' + arcLength;
function getParameterForTrim() {
var curve = new BezierCurve(elements.map(function(elem) {
return {x: +elem.getAttribute('cx'), y: +elem.getAttribute('cy')};
var pathTotalLength = curve.getArcLength();
var s = pathTotalLength - trimLen;
var t = curve.getParameterAtArcLength(s);
var pathLen = curve.getArcLength(t);
var p = curve.getPointAtParameter(t);
console.log('t', t, 'pathLen', pathLen, 'diff', (pathTotalLength - pathLen));
var splitPointCircleElem = document.getElementById('splitPoint');
splitPointCircleElem.setAttribute('cx', p.x);
splitPointCircleElem.setAttribute('cy', p.y);