These videos show simulated string with different properties.

Stiff string physics.


Stretchy string physics.

This is created using a 2d Verlet integrator. The string is composed of a line of points connected by springs. By applying different values to the springyness of the joints between the points and by altering the gravity value the behaviour of the string changes. This approach was first written about in 1791 by Delambre, but most recently rediscovered by Loup Verlet in 1967. It’s really very simple to implement.

method verlet()
	int a=0
	
	loop a<.points
		real oldx = .node_x[a]
		real oldy = .node_y[a]
		
		//apply verlet
		.node_x[a] = (.node_x[a]*2 - .node_oldx[a])
		.node_y[a] = (.node_y[a]*2 - .node_oldy[a]) + .gravity
		
		.node_oldx[a] = oldx
		.node_oldy[a] = oldy
		a++
	end
end

This is the core function. This goes through the list of nodes or points in the string individually and essentially inserts a force between the last known position and the new position we are calculating. No forces between nodes are considered at this point.

method constraints()
	int a=0
	real line_len_sq = 1.0*1.0
	//now scale the points towards the correct length
	loop a<.points
		//distance from this to the next point
		real deltax = .node_x[a] - .node_x[a+1]
		real deltay = .node_y[a] - .node_y[a+1]
		//generate a scale of how far off the correct length
		real vscale = line_len_sq / (deltax*deltax + deltay*deltay + line_len_sq) - 0.5
		//now move both points a fraction towards each other
		.node_x[a] += deltax * vscale
		.node_y[a] += deltay * vscale
		.node_x[a+1] -= deltax * vscale
		.node_y[a+1] -= deltay * vscale
		a++
	end
end

The constraints compare connected points. In this case we know it's a string so all the points are in order, so we go through each pair in order trying to adjust the position of the nodes so that they are the correct length apart.

method update()
	//first apply gravity inside verlet 
	.verlet()
	
	//then restitution, 
	//try and remake points the same distance apart
	.constraints()
		
	//then render
	.render()
end

This is the heartbeat, it essentially applies the gravity during the verlet pass, applies the constraints and then renders. Magic!