I am attempting to generate a 3d ribbon from a set of 3d points. The idea is to generate a realistic ribbon which follows those points.
In its current state, one example looks like this:

In this example I have procedural generated a path for the ribbon, pink lines show the normals at each point used to generate the ribbon. The problem is (for seemingly no reason) the ribbon does this twisting motion when switching from concave to convex. This twisting motion seems to have to do with the cross product flipping over, but I don't understand how to stop it from happening.
The algorithm used to generate the ribbon looks like this:
for (int i = 1; i < activeNodes.size()-1; i++) {
vec3 point = activeNodes[i].position;
vec3 ppoint = activeNodes[i-1].position;
vec3 npoint = activeNodes[i+1].position;
vec3 curVector = ppoint-point;
vec3 nextVector = point-npoint;
vec3 newAxis = glm::normalize(glm::cross(curVector,nextVector));
vec3 newNormal = glm::normalize(glm::cross(newAxis,nextVector));
if (i == 1) {
//Build previous too
nat(vcount) = newNormal;
vat(vcount++) = ppoint+newAxis*activeNodes[i-1].heightAbove;
nat(vcount) = newNormal;
vat(vcount++) = ppoint-newAxis*activeNodes[i-1].heightBelow;
}
vec3 newVertexA = point+newAxis*activeNodes[i].heightAbove;
vec3 newVertexB = point-newAxis*activeNodes[i].heightBelow;
//Build triangles
//Do stuff using newVertexA and newVertexB as the next two points in the ribbon
//no changes are made to those vertices during this
}
activeNodes is the list of points the ribbon is made of. I've written the algorithm in c++ but I'd be happy to receive answers in pseudo code, or just pure math.
Basically I need to limit the cross product which generates the newAxis to prevent it from flipping over like that. Any help would be greatly appreciated. One person suggested I use "null space" but was unable to elaborate on how, or even how that helps me.
Another view of the ribbon to help show the twisting:
