$ = document.querySelector.bind(document)
order = 3
w = $('section').offsetWidth
h = 480
r = Raphael "holder", w, h
interp = ([x1, y1], [x2, y2], t) ->
[
x1*(1-t) + x2*t,
y1*(1-t) + y2*t
]
nodes = []
curve = null
drawCurve = (t) ->
pts = ([n.attr('cx'), n.attr('cy')] for n in nodes)
points = (bezier pts, n for n in [0..t] by 0.01)
curve.attr path: 'M' + ("#{x} #{y}" for [x, y] in points).join('L')
bezier = (pts, t) ->
newpts = (interp pts[i], pts[i+1], t for i in [0..pts.length-2])
if newpts.length > 1
bezier newpts, t
else
newpts[0]
drawPoints = (pts, t) ->
newpts = (interp pts[i], pts[i+1], t for i in [0..pts.length-2])
if newpts.length > 1
hue = 0.1 + (order-newpts.length) / order
els = (r.circle(x, y, 5).attr(fill: "hsla(#{hue}, 0.8, 0.5)") for [x, y] in newpts)
els = els.concat drawPoints newpts, t
else
[x, y] = newpts[0]
els = [r.circle(x, y, 5).attr(fill: 'black')]
els.push r.path('M' + ("#{x} #{y}" for [x, y] in pts).join('L')).toBack()
els
paths = null
update = () ->
t = $('#slider').value
path.remove() for path in paths if paths?
paths = drawPoints([n.attr('cx'), n.attr('cy')] for n in nodes, t)
drawCurve(t)
node.toFront() for node in nodes
makeDraggy = (c) ->
odx = ody = 0
c.drag (dx, dy) ->
x = @attr('cx') + dx - odx
y = @attr('cy') + dy - ody
@attr cx: x, cy: y
[odx, ody] = [dx, dy]
update()
c.mouseup ->
odx = ody = 0
setup = ->
r.clear()
paths = null
curve = r.path().attr 'stroke-width': 3
myx = 0
nodes = for i in [0..order]
c = r.circle myx+=Math.random()*(w-20)/order, Math.random()*h, 6
c.attr fill:'red'
c.node.style.cursor = 'move'
makeDraggy(c)
update()
onOrder = ->
neworder = parseInt($('#order').value, 10)
if order != neworder
order = neworder
setup()
for e in ['change', 'input']
$('#slider').addEventListener e, update
$('#order').addEventListener e, onOrder
setup()
<div id='holder'></div>
<label for='slider'>T=</label>
<input id='slider'
type='range'
min='0'
max='1'
step='0.01'
value='0' /><br />
<label for='order'>Order</label>
<input id='order' type='number' min='1' max'100' step='1' value='3'/><br />
Drag red circles to move control points