I'm noticing that doesn't work every time because some paths are returning duplicate values for the [0] and [1] items in the bezier array. Check out this version. Notice that bez[0] and bez[1] are the same values....

Awesome, Blake! Thanks. My app offers an image rotation knob so I added conversion to percentage so I could add as rotation offset value.
Here's a forked pen with that added in. You can set the rotationOffset and it'll add it. I set the rotation offset to -10 degrees.

Using a bezier path, I'd love for there to be an option for autoRotate like immediateRender works for autoAlpha. In the pen, you see the plane starts off with no rotation and then abruptly rotates. Is there a way to accomplish this using any current settings? If I seek to something like 0.01, I get the initial rotation which is what I'd love to see right from the start.

One thing I'm trying to figure out that has me bumfuzzled - if you set the speed to something really slow, like 10, click to run the animation and quickly pause it, the initial shadow is coming in gray. Should this be a fromTo? Braincramp.

This is more of an offering for anyone interested in playing with it. In my app, I'm allowing users to pick a font-color and 2 shadow colors to form a shadow gradient. Using hsla color is the easiest way for me to parse through colors and alpha settings so used that. What I ran into doing a simple diff calculation of the color hue numbers was often an ugly rainbow gradient. For example, if you want to start with a hue of 350 and blend to a hue of 10 ( which are both in the red family), the closest path is 350 -> 0 -> 10. So, you can't just subtract 10 from 350. The codepen has some conditional code that finds the closest path.
If this helps someone, that's great. Just wanted to share. I know it could be greatly refactored though, so, if anyone wanted to fork and refactor, or offer a better solution, please do!

Thanks. For anyone else interested in this sort of thing, I ended up setting up a MutationObserver and it works like a charm. In config I set it to listen only to attributes and on the path there's a transform attribute. MutationObserver allows you to snag the old value so creating a delta was easy-peasy. You have to pass that in as part of the config setup though like...
config = {attributes: true, attributeOldValue: true}
It does return that old value as a string like "matrix(1 0 0 1 23 8)" so you have to get the x/y values out of that. Here's my code in case anyone is interested. I'll refactor it but this gets the job done. I have 2 things that have to be moved with it - a dragger and a wrapper for the image. The element IDs are stored in variables passed as an object to the function but this is the listener setup...
function setPathDragListener(B) {
// set up mutationObserver
let path = document.getElementById(B.settings.imageBez.pathID),
wrapper = document.getElementById(B.settings.imageBez.wrapperID),
dragger = B.dragger[0].target,
config = {attributes: true, attributeOldValue: true},
callback = function(mutationsList, observer) {
for( let mutation of mutationsList ) {
let matrix = mutation.target.getCTM();
if( mutation.oldValue === null ) return;
let old = mutation.oldValue;
let n = old.split(' ');
if( n === null || n[4] === undefined || n[5] === undefined ) return;
oldX = parseInt(n[4]);
oldY = parseInt(n[5].replace(')',''));
let xDiff = matrix.e - oldX;
let yDiff = matrix.f - oldY;
let wrapperX = wrapper._gsTransform.x,
wrapperY = wrapper._gsTransform.y;
TweenMax.set([dragger,wrapper],{x: (wrapperX + xDiff), y: (wrapperY + yDiff)});
}
};
let observer = new MutationObserver(callback);
observer.observe(path,config);
}

I know the PathEditor is undocumented and unsupported so this isn't a question regarding its use. The reason it is used is because I'm trying to use its onUpdate callback to update the position of the animated element (image in the codepen) as a path node is dragged. For example, if I drag the right-most node to the left (in the codepen), the plane should move to the left as it follows the change in the path's position change. Is there a ready-to-use function in GSAP to make this happen?