optimizations
diff --git a/player/index.html b/player/index.html
index a9cffd2..cb5f4c1 100644
--- a/player/index.html
+++ b/player/index.html
@@ -10,6 +10,7 @@
     <script src="js/utils/canvasPolyFill.js"></script>
     <script src="js/utils/functionExtensions.js"></script>
     <script src="js/utils/DataManager.js"></script>
+    <script src="js/utils/bez.js"></script>
     <script src="js/renderers/SVGRenderer.js"></script>
     <script src="js/renderers/CanvasRenderer.js"></script>
     <script src="js/effects.js"></script>
@@ -39,6 +40,6 @@
 </head>
 <body style="background-color:#666; margin: 10px;height: 100%; font-family: sans-serif;font-size: 10px">
 <div id="loader"></div>
-<div style="width:800px;height:800px;background-color:#cccccc" class="bodymovin" data-animation-path="exports/shapes" data-bm-player="0" data-anim-type="canvas" data-anim-loop="false"></div>
+<div style="width:800px;height:800px;background-color:#cccccc" class="bodymovin" data-animation-path="exports/ninja" data-bm-player="0" data-anim-type="canvas" data-anim-loop="true"></div>
 </body>
 </html>
diff --git a/player/js/animation/AnimationItem.js b/player/js/animation/AnimationItem.js
index d861b2e..f11c8b8 100644
--- a/player/js/animation/AnimationItem.js
+++ b/player/js/animation/AnimationItem.js
@@ -104,7 +104,7 @@
 AnimationItem.prototype.gotoFrame = function () {
     if(subframeEnabled){
         this.renderedFrames = [];
-        this.currentFrame = this.currentRawFrame;
+        this.currentFrame = Math.round(this.currentRawFrame*100)/100;
     }else{
         this.currentFrame = Math.floor(this.currentRawFrame);
     }
@@ -120,7 +120,7 @@
     }
     if(!this.renderedFrames[this.currentFrame]){
         this.renderedFrames[this.currentFrame] = true;
-        dataManager.renderFrame(this.layers,this.currentFrame);
+        dataManager.renderFrame(this.layers,this.currentFrame,this.animType);
     }
     this.renderer.renderFrame(this.currentFrame);
 };
diff --git a/player/js/elements/canvasElements/CVMaskElement.js b/player/js/elements/canvasElements/CVMaskElement.js
index 417d88c..218c2e1 100644
--- a/player/js/elements/canvasElements/CVMaskElement.js
+++ b/player/js/elements/canvasElements/CVMaskElement.js
@@ -36,12 +36,9 @@
     ctx.lineTo(-this.layerSize.w/2,this.layerSize.h-this.layerSize.h/2);
     ctx.lineTo(-this.layerSize.w/2,-this.layerSize.h/2);*/
     var j, jLen = data.v.length;
-    for (j = 0; j < jLen; j++) {
-        if (j == 0) {
-            ctx.moveTo(data.v[j][0], data.v[j][1]);
-        } else {
-            ctx.bezierCurveTo(data.o[j - 1][0] + data.v[j - 1][0], data.o[j - 1][1] + data.v[j - 1][1], data.i[j][0] + data.v[j][0], data.i[j][1] + data.v[j][1], data.v[j][0], data.v[j][1]);
-        }
+    ctx.moveTo(data.v[0][0], data.v[0][1]);
+    for (j = 1; j < jLen; j++) {
+        ctx.bezierCurveTo(data.o[j - 1][0] + data.v[j - 1][0], data.o[j - 1][1] + data.v[j - 1][1], data.i[j][0] + data.v[j][0], data.i[j][1] + data.v[j][1], data.v[j][0], data.v[j][1]);
     }
     ctx.bezierCurveTo(data.o[j - 1][0] + data.v[j - 1][0], data.o[j - 1][1] + data.v[j - 1][1], data.i[0][0] + data.v[0][0], data.i[0][1] + data.v[0][1], data.v[0][0], data.v[0][1]);
     ctx.closePath();
diff --git a/player/js/elements/canvasElements/CVShapeItemElement.js b/player/js/elements/canvasElements/CVShapeItemElement.js
index 1e1015c..7f6b162 100644
--- a/player/js/elements/canvasElements/CVShapeItemElement.js
+++ b/player/js/elements/canvasElements/CVShapeItemElement.js
@@ -48,6 +48,7 @@
         this.renderer.canvasContext.fill();
         this.renderer.canvasContext.stroke();
     }
+    delete this.renderedPaths[num];
     ctx.restore();
 };
 
@@ -76,11 +77,10 @@
     if(trimData.e == trimData.s){
         return;
     }
-    var ctx = this.renderer.canvasContext;
-    var path2d = new Path2D();
     if(this.renderedPaths[num]){
         return;
     }
+    var path2d = new Path2D();
     var animData = this.data.an;
     var path = animData.path[animData.path[num].forwardFrame];
     var pathNodes = path.pathNodes;
@@ -116,7 +116,6 @@
         })
     }
     var addedLength = 0;
-    //ctx.beginPath();
     var j, jLen,perc,flag, ended = false;
     var k, kLen = trims.length;
 
@@ -144,16 +143,12 @@
             for(k = 0; k < kLen; k+=1){
                 if(trims[k].s >= addedLength && trims[k].s < addedLength + segments[i].points[j+1].partialLength){
                     perc = ( trims[k].s - addedLength)/segments[i].points[j+1].partialLength;
-                    /*path2d.moveTo(segments[i].points[j].point[0]+(segments[i].points[j+1].point[0] - segments[i].points[j].point[0])*perc
-                        ,segments[i].points[j].point[1]+(segments[i].points[j+1].point[1] - segments[i].points[j].point[1])*perc);*/
-                    ctx.moveTo(segments[i].points[j].point[0]+(segments[i].points[j+1].point[0] - segments[i].points[j].point[0])*perc
+                    path2d.moveTo(segments[i].points[j].point[0]+(segments[i].points[j+1].point[0] - segments[i].points[j].point[0])*perc
                         ,segments[i].points[j].point[1]+(segments[i].points[j+1].point[1] - segments[i].points[j].point[1])*perc);
                 }
                 if(trims[k].e > addedLength && trims[k].e <= addedLength + segments[i].points[j+1].partialLength){
                     perc = ( trims[k].e - addedLength)/segments[i].points[j+1].partialLength;
-                    /*path2d.lineTo(segments[i].points[j].point[0]+(segments[i].points[j+1].point[0] - segments[i].points[j].point[0])*perc
-                        ,segments[i].points[j].point[1]+(segments[i].points[j+1].point[1] - segments[i].points[j].point[1])*perc);*/
-                    ctx.lineTo(segments[i].points[j].point[0]+(segments[i].points[j+1].point[0] - segments[i].points[j].point[0])*perc
+                    path2d.lineTo(segments[i].points[j].point[0]+(segments[i].points[j+1].point[0] - segments[i].points[j].point[0])*perc
                         ,segments[i].points[j].point[1]+(segments[i].points[j+1].point[1] - segments[i].points[j].point[1])*perc);
                     trims.splice(k,1);
                     k -= 1;
@@ -163,12 +158,11 @@
                         break;
                     }
                 }else if(addedLength > trims[k].s && addedLength < trims[k].e){
-                    //path2d.lineTo(segments[i].points[j].point[0],segments[i].points[j].point[1]);
-                    ctx.lineTo(segments[i].points[j].point[0],segments[i].points[j].point[1]);
+                    path2d.lineTo(segments[i].points[j].point[0],segments[i].points[j].point[1]);
                 }
             }
         }
-        //this.renderedPaths[num] = path2d;
+        this.renderedPaths[num] = path2d;
     }
 };
 
@@ -176,6 +170,11 @@
     var animData = this.data.an;
     var path = animData.path[animData.path[num].forwardFrame];
 
+    if(this.renderedPaths[num]){
+        return;
+    }
+    var path2d = new Path2D();
+
     animData.renderedFrame.path = path.pathString;
     var ctx = this.renderer.canvasContext;
 
@@ -187,19 +186,19 @@
     ctx.beginPath();
     for(i=0;i<len;i+=1){
         if(i == 0){
-            ctx.moveTo(pathNodes.v[i][0],pathNodes.v[i][1]);
+            path2d.moveTo(pathNodes.v[i][0],pathNodes.v[i][1]);
         }else{
-            ctx.bezierCurveTo(pathNodes.o[i-1][0]+pathNodes.v[i-1][0],pathNodes.o[i-1][1]+pathNodes.v[i-1][1]
+            path2d.bezierCurveTo(pathNodes.o[i-1][0]+pathNodes.v[i-1][0],pathNodes.o[i-1][1]+pathNodes.v[i-1][1]
                 ,pathNodes.i[i][0]+pathNodes.v[i][0],pathNodes.i[i][1]+pathNodes.v[i][1]
                 ,pathNodes.v[i][0],pathNodes.v[i][1]);
         }
     }
     if(path.closed){
-    ctx.bezierCurveTo(pathNodes.o[i-1][0]+pathNodes.v[i-1][0],pathNodes.o[i-1][1]+pathNodes.v[i-1][1]
+        path2d.bezierCurveTo(pathNodes.o[i-1][0]+pathNodes.v[i-1][0],pathNodes.o[i-1][1]+pathNodes.v[i-1][1]
         ,pathNodes.i[0][0]+pathNodes.v[0][0],pathNodes.i[0][1]+pathNodes.v[0][1]
         ,pathNodes.v[0][0],pathNodes.v[0][1]);
     }
-    ctx.closePath();
+    this.renderedPaths[num] = path2d;
 };
 
 CVShapeItemElement.prototype.renderEllipse = function(num){
diff --git a/player/js/utils/DataManager.js b/player/js/utils/DataManager.js
index e47a010..30089cc 100644
--- a/player/js/utils/DataManager.js
+++ b/player/js/utils/DataManager.js
@@ -1,6 +1,5 @@
 var dataManager = (function(){
     var frameRate = 0;
-    var easingFunctions = {};
     var matrixInstance =  new MatrixManager();
     var storedBezierCurves = {};
 
@@ -48,40 +47,6 @@
         return string;
     }
 
-    function bez(encodedFuncName, coOrdArray) {
-        coOrdArray = encodedFuncName;
-        encodedFuncName = 'bez_' + coOrdArray.join('_').replace(/\./g, 'p');
-        if(easingFunctions[encodedFuncName]){
-            return encodedFuncName;
-        }
-        var	polyBez = function(p1, p2) {
-            var A = [null, null], B = [null, null], C = [null, null],
-                bezCoOrd = function(t, ax) {
-                    C[ax] = 3 * p1[ax], B[ax] = 3 * (p2[ax] - p1[ax]) - C[ax], A[ax] = 1 - C[ax] - B[ax];
-                    return t * (C[ax] + t * (B[ax] + t * A[ax]));
-                },
-                xDeriv = function(t) {
-                    return C[0] + t * (2 * B[0] + 3 * A[0] * t);
-                },
-                xForT = function(t) {
-                    var x = t, i = 0, z;
-                    while (++i < 14) {
-                        z = bezCoOrd(x, 0) - t;
-                        if (Math.abs(z) < 1e-3) break;
-                        x -= z / xDeriv(x);
-                    }
-                    return x;
-                };
-            return function(t) {
-                return bezCoOrd(xForT(t), 1);
-            }
-        };
-        easingFunctions[encodedFuncName] = function(x, t, b, c, d) {
-            return c * polyBez([coOrdArray[0], coOrdArray[1]], [coOrdArray[2], coOrdArray[3]])(t/d) + b;
-        };
-        return encodedFuncName;
-    }
-
     function getInterpolatedValues(keyframes, frameCount, offsetTime){
         var i ,len;
         var valuesArray = [];
@@ -94,7 +59,6 @@
         var count;
         var propertyArray = [];
         var j, jLen;
-        var easingFnName;
         len = keyframes.length;
         var perc;
         var curveSegments = 1000;
@@ -166,8 +130,7 @@
                 }else if(i>=keyData.t && i<nextKeyData.t){
                     propertyArray = [];
                     if(keyData.to){
-                        easingFnName = bez([keyData.o.x,keyData.o.y,keyData.i.x,keyData.i.y]);
-                        perc = easingFunctions[easingFnName]('',i-keyData.t,0,1,nextKeyData.t-keyData.t);
+                        perc = bez.getCurve([keyData.o.x,keyData.o.y,keyData.i.x,keyData.i.y])('',i-keyData.t,0,1,nextKeyData.t-keyData.t);
                         var bezierData = keyData.bezierData;
                         var distanceInLine = bezierData.segmentLength*perc;
                         var k, kLen, segmentPerc;
@@ -206,8 +169,7 @@
                                     inX = keyData.i.x;
                                     inY = keyData.i.y;
                                 }
-                                easingFnName = bez([outX,outY,inX,inY]);
-                                perc = easingFunctions[easingFnName]('',i-keyData.t,0,1,nextKeyData.t-keyData.t);
+                                perc = bez.getCurve([outX,outY,inX,inY])('',i-keyData.t,0,1,nextKeyData.t-keyData.t);
                             }
                             // for shapes
                             if(startItem.i){
@@ -373,6 +335,8 @@
         bezierData.segmentLength = addedLength;
     }
 
+    var keyData, nextKeyData,propertyArray,bezierData;
+
     function getInterpolatedValue(keyframes, frameNum, offsetTime){
 
         //Todo save on each frame the point found. pass to this function the previous frame to iterate points from last point.
@@ -380,7 +344,7 @@
         if(!(keyframes instanceof Array) || keyframes[0].t == null){
             return keyframes;
         }
-        var i = 0, len = keyframes.length-1, keyData, nextKeyData;
+        var i = 0, len = keyframes.length-1;
         while(i<len){
             keyData = keyframes[i];
             nextKeyData = keyframes[i+1];
@@ -394,16 +358,16 @@
             buildBezierData(keyData);
         }
         var k, kLen;
-        var easingFnName, perc, j = 0, propertyArray = [];
+        var easingFnName, perc, j = 0;
+        propertyArray = [];
         if(keyData.to){
-            var bezierData = keyData.bezierData;
+            bezierData = keyData.bezierData;
             if(frameNum >= nextKeyData.t-offsetTime){
                 return bezierData.points[bezierData.points.length - 1].point;
             }else if(frameNum < keyData.t-offsetTime){
                 return bezierData.points[0].point;
             }
-            easingFnName = bez([keyData.o.x,keyData.o.y,keyData.i.x,keyData.i.y]);
-            perc = easingFunctions[easingFnName]('',(frameNum)-(keyData.t-offsetTime),0,1,(nextKeyData.t-offsetTime)-(keyData.t-offsetTime));
+            perc = bez.getCurve([keyData.o.x,keyData.o.y,keyData.i.x,keyData.i.y])('',(frameNum)-(keyData.t-offsetTime),0,1,(nextKeyData.t-offsetTime)-(keyData.t-offsetTime));
             var distanceInLine = bezierData.segmentLength*perc;
             var segmentPerc;
             while(j<bezierData.points.length){
@@ -439,8 +403,7 @@
                         inX = keyData.i.x;
                         inY = keyData.i.y;
                     }
-                    easingFnName = bez([outX,outY,inX,inY]);
-                    perc = easingFunctions[easingFnName]('',(frameNum)-(keyData.t-offsetTime),0,1,(nextKeyData.t-offsetTime)-(keyData.t-offsetTime));
+                    perc = bez.getCurve([outX,outY,inX,inY])('',(frameNum)-(keyData.t-offsetTime),0,1,(nextKeyData.t-offsetTime)-(keyData.t-offsetTime));
                     if(frameNum >= nextKeyData.t-offsetTime){
                         perc = 1;
                     }else if(frameNum < keyData.t-offsetTime){
@@ -489,6 +452,8 @@
     }
 
     var pathV,pathO,pathI;
+    var pathString = '';
+    var pathData;
 
     function createPathString(paths,closed){
         if(!(paths instanceof Array)){
@@ -496,43 +461,20 @@
         }
         var l,lLen = paths.length;
         var k, kLen;
-        var pathString = '';
-        var pathData;
+        pathString = '';
         for(l = 0;l<lLen;l+=1){
             pathData = paths[l];
             pathV = pathData.v;
             pathO = pathData.o;
             pathI = pathData.i;
             kLen = pathV.length;
-            for(k=0;k<kLen;k++){
-                if(k==0){
-                    pathString += "M"+pathV[k][0]+","+pathV[k][1];
-                }else{
-                    pathString += " C"+(pathO[k-1][0]+pathV[k-1][0])+","+(pathO[k-1][1]+pathV[k-1][1]);
-                    pathString += " "+(pathI[k][0]+pathV[k][0])+","+(pathI[k][1]+pathV[k][1]);
-                    pathString += " "+pathV[k][0]+","+pathV[k][1];
-                }
+            pathString += "M"+pathV[0][0]+","+pathV[0][1];
+            for(k=1;k<kLen;k++){
+                pathString += " C"+(pathO[k-1][0]+pathV[k-1][0])+","+(pathO[k-1][1]+pathV[k-1][1]) + " "+(pathI[k][0]+pathV[k][0])+","+(pathI[k][1]+pathV[k][1]) + " "+pathV[k][0]+","+pathV[k][1];
             }
             if(closed !== false){
-                pathString += " C"+(pathO[k-1][0]+pathV[k-1][0])+","+(pathO[k-1][1]+pathV[k-1][1]);
-                pathString += " "+(pathI[0][0]+pathV[0][0])+","+(pathI[0][1]+pathV[0][1]);
-                pathString += " "+pathV[0][0]+","+(pathV[0][1]);
+                pathString += " C"+(pathO[k-1][0]+pathV[k-1][0])+","+(pathO[k-1][1]+pathV[k-1][1]) + " "+(pathI[0][0]+pathV[0][0])+","+(pathI[0][1]+pathV[0][1]) + " "+pathV[0][0]+","+(pathV[0][1]);
             }
-            /*
-             for(k=0;k<kLen;k++){
-             if(k==0){
-             pathString += "M"+Math.round(10*(pathV[k][0]))/10+","+Math.round(10*(pathV[k][1]))/10;
-             }else{
-             pathString += " C"+Math.round(10*(pathO[k-1][0]+pathV[k-1][0]))/10+","+Math.round(10*(pathO[k-1][1]+pathV[k-1][1]))/10;
-             pathString += " "+Math.round(10*(pathI[k][0]+pathV[k][0]))/10+","+Math.round(10*(pathI[k][1]+pathV[k][1]))/10;
-             pathString += " "+Math.round(10*(pathV[k][0]))/10+","+Math.round(10*(pathV[k][1]))/10;
-             }
-             }
-             if(closed !== false){
-             pathString += " C"+Math.round(10*(pathO[k-1][0]+pathV[k-1][0]))/10+","+Math.round(10*(pathO[k-1][1]+pathV[k-1][1]))/10;
-             pathString += " "+Math.round(10*(pathI[0][0]+pathV[0][0]))/10+","+Math.round(10*(pathI[0][1]+pathV[0][1]))/10;
-             pathString += " "+Math.round(10*(pathV[0][0]))/10+","+Math.round(10*(pathV[0][1]))/10;
-             }*/
         }
         return pathString;
     }
@@ -542,8 +484,9 @@
     var timeRemapped;
     var shapeItem;
     var fillOpacity,fillColor, shape, strokeColor, strokeOpacity, strokeWidth, elmPos, elmSize, elmRound;
+    var shapeTrOb = {};
 
-    function iterateLayers(layers, frameNum){
+    function iterateLayers(layers, frameNum,renderType){
 
         var offsettedFrameNum, i, len;
         var j, jLen = layers.length, item;
@@ -587,7 +530,9 @@
                     }
                     maskValue = getInterpolatedValue(maskProps[i].pt,offsettedFrameNum, item.startTime);
                     maskProps[i].pathVertices[offsettedFrameNum] = maskValue instanceof Array ? maskValue : [maskValue];
-                    maskProps[i].pathStrings[offsettedFrameNum] = createPathString(maskValue,maskProps[i].cl);
+                    if(renderType == 'svg'){
+                        maskProps[i].pathStrings[offsettedFrameNum] = createPathString(maskValue,maskProps[i].cl);
+                    }
                     maskProps[i].opacity[offsettedFrameNum] = getInterpolatedValue(maskProps[i].o,offsettedFrameNum, item.startTime);
                     maskProps[i].opacity[offsettedFrameNum] = maskProps[i].opacity[offsettedFrameNum] instanceof Array ? maskProps[i].opacity[offsettedFrameNum][0]/100 : maskProps[i].opacity[offsettedFrameNum]/100;
                 }
@@ -631,11 +576,13 @@
                     if(shapeItem.ks){
                         shape = getInterpolatedValue(shapeItem.ks,offsettedFrameNum, item.startTime);
                         shapeItem.an.path[offsettedFrameNum] = {
-                            pathString : createPathString(shape,shapeItem.closed),
                             pathNodes: shape,
                             closed: shapeItem.closed,
                             forwardFrame : offsettedFrameNum
                         };
+                        if(renderType == 'svg'){
+                            shapeItem.an.path[offsettedFrameNum].pathString = createPathString(shape,shapeItem.closed);
+                        }
                     }else if(shapeItem.el){
                         elmPos = getInterpolatedValue(shapeItem.el.p,offsettedFrameNum, item.startTime);
                         elmSize = getInterpolatedValue(shapeItem.el.s,offsettedFrameNum, item.startTime);
@@ -666,7 +613,7 @@
                             forwardFrame : offsettedFrameNum
                         };
                     }
-                    var shapeTrOb = {};
+                    shapeTrOb = {};
                     //var shapeDataOb = {};
                     shapeTrOb.a = getInterpolatedValue(shapeItem.tr.a,offsettedFrameNum, item.startTime);
                     shapeTrOb.o = getInterpolatedValue(shapeItem.tr.o,offsettedFrameNum, item.startTime);
@@ -700,8 +647,8 @@
         }
     }
 
-    function renderFrame(layers,num){
-        iterateLayers(layers, num);
+    function renderFrame(layers,num, renderType){
+        iterateLayers(layers, num, renderType);
     }
 
     var moduleOb = {};
diff --git a/player/js/utils/canvasPolyFill.js b/player/js/utils/canvasPolyFill.js
index a03f530..1762ce5 100644
--- a/player/js/utils/canvasPolyFill.js
+++ b/player/js/utils/canvasPolyFill.js
@@ -10,7 +10,6 @@
         }
     }
     if(!supportsPath2D){
-        console.log('supportsPath2D: ',supportsPath2D);
         function Path2D_(){
             this.commandArrays = [];
         }
@@ -21,8 +20,8 @@
             this.commandArrays.push({type:'line',coord:[x,y]})
         };
 
-        Path2D_.prototype.bezierCurveTo = function(cx,cy,x,y){
-            this.commandArrays.push({type:'bezierCurve',coord:[cx,cy,x,y]})
+        Path2D_.prototype.bezierCurveTo = function(cx,cy,cx2,cy2,x,y){
+            this.commandArrays.push({type:'bezierCurve',coord:[cx,cy,cx2,cy2,x,y]})
         };
 
         Path2D_.prototype.drawToContext = function(ctx){
@@ -38,11 +37,10 @@
                         ctx.lineTo(command.coord[0],command.coord[1]);
                         break;
                     case "bezierCurve":
-                        ctx.bezierCurveTo(command.coord[0],command.coord[1],command.coord[2],command.coord[3]);
+                        ctx.bezierCurveTo(command.coord[0],command.coord[1],command.coord[2],command.coord[3],command.coord[4],command.coord[5]);
                         break;
                 }
             }
-            ctx.closePath();
         };
         Path2D = Path2D_;
     }
diff --git a/player/js/utils/common.js b/player/js/utils/common.js
index f6bb0da..bf7d32d 100644
--- a/player/js/utils/common.js
+++ b/player/js/utils/common.js
@@ -1,12 +1,8 @@
 var subframeEnabled = false;
-var svgElement = document.createElementNS(svgNS,'svg');
-var shapeHelper = document.createElementNS(svgNS,'path');
 var supportsPath2D = typeof Path2D === 'function';
 supportsPath2D = false;
 var body;
 
-svgElement.appendChild(shapeHelper);
-
 function styleDiv(element){
     element.style.position = "absolute";
     element.style.top = 0;
@@ -52,13 +48,4 @@
         g: parseInt(result[2], 16),
         b: parseInt(result[3], 16)
     } : null;
-}
-
-function getPathSize(path){
-    if(!body){
-        body = document.getElementsByTagName('body')[0];
-        body.appendChild(svgElement);
-    }
-    shapeHelper.setAttribute('d',path);
-    return svgElement.getBBox();
-}
+}
\ No newline at end of file