performance
diff --git a/player/index.html b/player/index.html
index cb5f4c1..d9baebd 100644
--- a/player/index.html
+++ b/player/index.html
@@ -40,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/ninja" data-bm-player="0" data-anim-type="canvas" data-anim-loop="true"></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="true"></div>
 </body>
 </html>
diff --git a/player/js/3rd_party/transformation-matrix.js b/player/js/3rd_party/transformation-matrix.js
index 9bd83e1..67fc5ea 100644
--- a/player/js/3rd_party/transformation-matrix.js
+++ b/player/js/3rd_party/transformation-matrix.js
@@ -36,6 +36,8 @@
 
     me.context = context;
 
+    me.cos = me.sin = 0;
+
     // reset canvas transformations (if any) to enable 100% sync.
     if (context) context.setTransform(1, 0, 0, 1, 0, 0);
 }
@@ -100,9 +102,12 @@
      * @param {number} angle - angle in radians
      */
     rotate: function(angle) {
-        var cos = Math.cos(angle),
-            sin = Math.sin(angle);
-        return this._t(cos, sin, -sin, cos, 0, 0);
+        if(angle == 0){
+            return this;
+        }
+        this.cos = Math.cos(angle);
+        this.sin = Math.sin(angle);
+        return this._t(this.cos, this.sin, -this.sin, this.cos, 0, 0);
     },
 
     /**
@@ -138,6 +143,9 @@
      * @param {number} sy - scale factor y (1 does nothing)
      */
     scale: function(sx, sy) {
+        if(sx == 1 && sy == 1){
+            return this;
+        }
         return this._t(sx, 0, 0, sy, 0, 0);
     },
 
@@ -217,14 +225,13 @@
      * @param {number} f - translate y
      */
     setTransform: function(a, b, c, d, e, f) {
-        var me = this;
-        me.a = a;
-        me.b = b;
-        me.c = c;
-        me.d = d;
-        me.e = e;
-        me.f = f;
-        return me._x();
+        this.a = a;
+        this.b = b;
+        this.c = c;
+        this.d = d;
+        this.e = e;
+        this.f = f;
+        return this._x();
     },
 
     /**
@@ -263,27 +270,26 @@
      */
     transform: function(a2, b2, c2, d2, e2, f2) {
 
-        var me = this,
-            a1 = me.a,
-            b1 = me.b,
-            c1 = me.c,
-            d1 = me.d,
-            e1 = me.e,
-            f1 = me.f;
+        var a1 = this.a,
+            b1 = this.b,
+            c1 = this.c,
+            d1 = this.d,
+            e1 = this.e,
+            f1 = this.f;
 
         /* matrix order (canvas compatible):
          * ace
          * bdf
          * 001
          */
-        me.a = a1 * a2 + c1 * b2;
-        me.b = b1 * a2 + d1 * b2;
-        me.c = a1 * c2 + c1 * d2;
-        me.d = b1 * c2 + d1 * d2;
-        me.e = a1 * e2 + c1 * f2 + e1;
-        me.f = b1 * e2 + d1 * f2 + f1;
+        this.a = a1 * a2 + c1 * b2;
+        this.b = b1 * a2 + d1 * b2;
+        this.c = a1 * c2 + c1 * d2;
+        this.d = b1 * c2 + d1 * d2;
+        this.e = a1 * e2 + c1 * f2 + e1;
+        this.f = b1 * e2 + d1 * f2 + f1;
 
-        return me._x();
+        return this._x();
     },
 
     /**
@@ -672,8 +678,7 @@
      * @returns {Array}
      */
     toArray: function() {
-        var me = this;
-        return [me.a, me.b, me.c, me.d, me.e, me.f];
+        return [this.a, this.b, this.c, this.d, this.e, this.f];
     },
 
     /**
@@ -681,7 +686,7 @@
      * @returns {string}
      */
     toCSS: function() {
-        return "matrix(" + this.toArray() + ")";
+        return "matrix(" + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ")";
     },
 
     /**
@@ -717,9 +722,8 @@
      * @private
      */
     _x: function() {
-        var me = this;
-        if (me.context)
-            me.context.setTransform(me.a, me.b, me.c, me.d, me.e, me.f);
-        return me;
+        if (this.context)
+            this.context.setTransform(this.a, this.b, this.c, this.d, this.e, this.f);
+        return this;
     }
 };
\ No newline at end of file
diff --git a/player/js/animation/AnimationItem.js b/player/js/animation/AnimationItem.js
index f11c8b8..4bb6b11 100644
--- a/player/js/animation/AnimationItem.js
+++ b/player/js/animation/AnimationItem.js
@@ -103,7 +103,7 @@
 
 AnimationItem.prototype.gotoFrame = function () {
     if(subframeEnabled){
-        this.renderedFrames = [];
+        //this.renderedFrames = [];
         this.currentFrame = Math.round(this.currentRawFrame*100)/100;
     }else{
         this.currentFrame = Math.floor(this.currentRawFrame);
diff --git a/player/js/elements/canvasElements/CVBaseElement.js b/player/js/elements/canvasElements/CVBaseElement.js
index d04a16a..9571579 100644
--- a/player/js/elements/canvasElements/CVBaseElement.js
+++ b/player/js/elements/canvasElements/CVBaseElement.js
@@ -43,10 +43,6 @@
         return false;
     }
     var ctx = this.renderer.canvasContext;
-    /*console.log('this.data.layerName: ',this.data.layerName);
-    console.log('this.data.width: ',this.data.width);
-    console.log('this.data.height: ',this.data.height);*/
-    //ctx.translate(-this.data.width/2,-this.data.height/2);
     if(this.data.parentHierarchy){
         var i, len = this.data.parentHierarchy.length, animData;
         for(i = len - 1; i>=0 ; i -= 1){
@@ -58,7 +54,6 @@
         }
     }
 
-     //ctx.translate(this.currentAnimData.tr.a[0],this.currentAnimData.tr.a[1]);
     ctx.globalAlpha = this.currentAnimData.tr.o;
     var matrixValue = this.currentAnimData.matrixArray;
      ctx.transform(matrixValue[0], matrixValue[1], matrixValue[2], matrixValue[3], matrixValue[4], matrixValue[5]);
diff --git a/player/js/elements/canvasElements/CVShapeItemElement.js b/player/js/elements/canvasElements/CVShapeItemElement.js
index 7f6b162..7ac9bc6 100644
--- a/player/js/elements/canvasElements/CVShapeItemElement.js
+++ b/player/js/elements/canvasElements/CVShapeItemElement.js
@@ -3,6 +3,8 @@
     this.renderer = renderer;
     this.frameNum = -1;
     this.renderedPaths = {};
+    this.trims = [];
+    this.trimPos = 0;
 }
 
 CVShapeItemElement.prototype.adjustTrim = function(){
@@ -18,8 +20,7 @@
 CVShapeItemElement.prototype.renderShape = function(){
     var num = this.frameNum;
     var ctx = this.renderer.canvasContext;
-    ctx.save();
-    this.renderTransform(num);
+    var flag = this.renderTransform(num);
     if(this.data.type=="pathShape"){
         if(this.data.trim){
             this.renderTrimPath(num);
@@ -48,8 +49,9 @@
         this.renderer.canvasContext.fill();
         this.renderer.canvasContext.stroke();
     }
-    delete this.renderedPaths[num];
-    ctx.restore();
+    if(flag){
+        ctx.restore();
+    }
 };
 
 CVShapeItemElement.prototype.prepareFrame = function(num){
@@ -63,15 +65,25 @@
         var tr = animData.tr[animData.tr[num].forwardFrame];
         animData.renderedFrame.tr = tr.forwardFrame;
         var matrixValue = tr.mtArr;
-        //ctx.translate(tr.a[0],tr.a[1]);
+        if(matrixValue[0] == 1 && matrixValue[1] == 0 && matrixValue[2] == 0 && matrixValue[3] == 1 && matrixValue[4] == 0 && matrixValue[5] == 0){
+            return false;
+        }
+        ctx.save();
         ctx.transform(matrixValue[0], matrixValue[1], matrixValue[2], matrixValue[3], matrixValue[4], matrixValue[5]);
         ctx.translate(-tr.a[0],-tr.a[1]);
-        //ctx.translate(-tr.a[0],-tr.a[1]);
-        //ctx.translate(-tr.a[0],-tr.a[1]);
-
+        return true;
     }
 };
 
+CVShapeItemElement.prototype.addToTrim = function(pos,s,e){
+    if(!this.trims[pos]){
+        this.trims.push({});
+    }
+    this.trims[pos].s = s;
+    this.trims[pos].e = e;
+    this.trims[pos].ended = false;
+};
+
 CVShapeItemElement.prototype.renderTrimPath = function(num){
     var trimData = this.data.trim.an[this.data.trim.an[num].forwardFrame];
     if(trimData.e == trimData.s){
@@ -88,36 +100,29 @@
     var totalLength = 0;
     var i, len = pathNodes.v.length;
     for(i = 0; i < len - 1; i += 1){
-        segments.push(dataManager.drawBezierCurve([pathNodes.v[i],pathNodes.v[i+1],pathNodes.o[i],pathNodes.i[i+1]]));
+        segments.push(bez.drawBezierCurve(pathNodes.v[i],pathNodes.v[i+1],pathNodes.o[i],pathNodes.i[i+1]));
         totalLength += segments[i].segmentLength;
     }
     if(path.closed){
-        segments.push(dataManager.drawBezierCurve([pathNodes.v[len - 1],pathNodes.v[0],pathNodes.o[len - 1],pathNodes.i[0]]));
+        segments.push(bez.drawBezierCurve(pathNodes.v[len - 1],pathNodes.v[0],pathNodes.o[len - 1],pathNodes.i[0]));
         totalLength += segments[i].segmentLength;
     }
     len = segments.length;
     var segmentLength = totalLength*(trimData.e - trimData.s)/100;
     var offset = ((trimData.s/100 + (trimData.o%360)/360)%1)*totalLength;
-    var trims = [];
+    var endedCount = 0;
     if(offset + segmentLength - totalLength > 0.00001){
         var secondarySegment = offset + segmentLength - totalLength;
-        trims.push({
-            s: offset,
-            e: offset + segmentLength - secondarySegment
-        });
-        trims.push({
-            s: 0,
-            e: offset + segmentLength - totalLength
-        })
+        this.addToTrim(0,offset,offset + segmentLength - secondarySegment);
+        this.addToTrim(1,0,offset + segmentLength - totalLength);
+        endedCount += 2;
     }else{
-        trims.push({
-            s: offset,
-            e: offset + segmentLength
-        })
+        this.addToTrim(0,offset,offset + segmentLength);
+        endedCount += 1;
     }
     var addedLength = 0;
     var j, jLen,perc,flag, ended = false;
-    var k, kLen = trims.length;
+    var k, kLen = this.trims.length;
 
     for(i = 0; i < len; i += 1){
         if(ended){
@@ -126,7 +131,7 @@
         jLen = segments[i].points.length;
         flag = true;
         for(k = 0; k < kLen; k+=1){
-            if(addedLength + segments[i].segmentLength > trims[k].s){
+            if(addedLength + segments[i].segmentLength > this.trims[k].s){
                 flag = false;
             }
         }
@@ -138,26 +143,28 @@
             if(ended){
                 break;
             }
-            kLen = trims.length;
+            kLen = this.trims.length;
             addedLength += segments[i].points[j].partialLength;
             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;
+                if(this.trims[k].ended){
+                    continue;
+                }
+                if(this.trims[k].s >= addedLength && this.trims[k].s < addedLength + segments[i].points[j+1].partialLength){
+                    perc = ( this.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);
                 }
-                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;
+                if(this.trims[k].e > addedLength && this.trims[k].e <= addedLength + segments[i].points[j+1].partialLength){
+                    perc = ( this.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);
-                    trims.splice(k,1);
-                    k -= 1;
-                    kLen -= 1;
-                    if(kLen == 0){
+                    endedCount -= 1;
+                    this.trims[k].ended = false;
+                    if(endedCount == 0){
                         ended = true;
                         break;
                     }
-                }else if(addedLength > trims[k].s && addedLength < trims[k].e){
+                }else if(addedLength > this.trims[k].s && addedLength < this.trims[k].e){
                     path2d.lineTo(segments[i].points[j].point[0],segments[i].points[j].point[1]);
                 }
             }
@@ -184,14 +191,11 @@
     }
     var i,len = pathNodes.i.length;
     ctx.beginPath();
-    for(i=0;i<len;i+=1){
-        if(i == 0){
-            path2d.moveTo(pathNodes.v[i][0],pathNodes.v[i][1]);
-        }else{
-            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]);
-        }
+    path2d.moveTo(pathNodes.v[0][0],pathNodes.v[0][1]);
+    for(i=1;i<len;i+=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){
         path2d.bezierCurveTo(pathNodes.o[i-1][0]+pathNodes.v[i-1][0],pathNodes.o[i-1][1]+pathNodes.v[i-1][1]
@@ -244,16 +248,15 @@
         var fill = animData.fill[animData.fill[num].forwardFrame];
         animData.renderedFrame.fill = {color:fill.color,opacity:fill.opacity};
         if(this.data.fillEnabled!==false){
-            var rgbColor = hexToRgb(fill.color);
-            this.renderer.canvasContext.fillStyle='rgba('+rgbColor.r+','+rgbColor.g+','+rgbColor.b+','+fill.opacity+')';
-        }else{
-            this.renderer.canvasContext.fillStyle='rgba(0,0,0,0)';
+            if(fill.opacity < 1){
+                this.renderer.canvasContext.fillStyle=fillToRgba(fill.color, fill.opacity);
+            }else{
+                this.renderer.canvasContext.fillStyle=fill.color;
+            }
+            return;
         }
-    }else{
-        this.renderer.canvasContext.fillStyle='rgba(0,0,0,0)';
     }
-    //this.renderer.canvasContext.fillStyle='rgba(255,0,0,0)';
-    //this.renderer.canvasContext.globalAlpha = .3;
+    this.renderer.canvasContext.fillStyle='rgba(0,0,0,0)';
 };
 
 CVShapeItemElement.prototype.renderStroke = function(num){
@@ -265,12 +268,13 @@
          */
         this.renderer.canvasContext.lineWidth=stroke.width;
         if(this.data.strokeEnabled!==false){
-            var rgbColor = hexToRgb(stroke.color);
-            this.renderer.canvasContext.strokeStyle='rgba('+rgbColor.r+','+rgbColor.g+','+rgbColor.b+','+stroke.opacity+')';
-        }else{
-            this.renderer.canvasContext.strokeStyle='rgba(0,0,0,0)';
+            if(stroke.opacity < 1){
+                this.renderer.canvasContext.strokeStyle=fillToRgba(stroke.color, stroke.opacity);
+            }else{
+                this.renderer.canvasContext.strokeStyle=stroke.color;
+            }
+            return;
         }
-    }else{
-        this.renderer.canvasContext.strokeStyle = 'rgba(0,0,0,0)';
     }
+    this.renderer.canvasContext.strokeStyle = 'rgba(0,0,0,0)';
 };
diff --git a/player/js/utils/DataManager.js b/player/js/utils/DataManager.js
index 30089cc..52adf1e 100644
--- a/player/js/utils/DataManager.js
+++ b/player/js/utils/DataManager.js
@@ -64,6 +64,7 @@
         var curveSegments = 1000;
         var absToCoord = [];
         var absTiCoord = [];
+        //len = keyframes.length;
         keyframes.forEach(function(keyData){
             keyData.t -= offsetTime;
             if(keyData.to){
@@ -130,7 +131,7 @@
                 }else if(i>=keyData.t && i<nextKeyData.t){
                     propertyArray = [];
                     if(keyData.to){
-                        perc = bez.getCurve([keyData.o.x,keyData.o.y,keyData.i.x,keyData.i.y])('',i-keyData.t,0,1,nextKeyData.t-keyData.t);
+                        perc = bez.getEasingCurve([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;
@@ -169,7 +170,7 @@
                                     inX = keyData.i.x;
                                     inY = keyData.i.y;
                                 }
-                                perc = bez.getCurve([outX,outY,inX,inY])('',i-keyData.t,0,1,nextKeyData.t-keyData.t);
+                                perc = bez.getEasingCurve([outX,outY,inX,inY])('',i-keyData.t,0,1,nextKeyData.t-keyData.t);
                             }
                             // for shapes
                             if(startItem.i){
@@ -235,106 +236,6 @@
         return valuesArray;
     }
 
-    function drawBezierCurve(coOrdArray){
-        var bezierName = coOrdArray.join('_').replace(/\./g, 'p');
-        if(storedBezierCurves[bezierName]){
-
-            return storedBezierCurves[bezierName];
-        }
-        var curveSegments = 1000, absToCoord, absTiCoord;
-        var k;
-        var triCoord1,triCoord2,triCoord3,liCoord1,liCoord2,ptCoord,perc,addedLength = 0;
-        var ptDistance;
-        var point,lastPoint = null;
-        var bezierData = {
-            points :[],
-            segmentLength: 0
-        };
-        if(pointOnLine2D(coOrdArray[0][0],coOrdArray[0][1],coOrdArray[1][0],coOrdArray[1][1],coOrdArray[0][0]+coOrdArray[2][0],coOrdArray[0][1]+coOrdArray[2][1])
-            && pointOnLine2D(coOrdArray[0][0],coOrdArray[0][1],coOrdArray[1][0],coOrdArray[1][1],coOrdArray[1][0]+coOrdArray[3][0],coOrdArray[1][1]+coOrdArray[3][1])){
-            curveSegments = 2;
-        }
-        for(k=0;k<curveSegments;k+=1){
-            point = [];
-            perc = k/(curveSegments-1);
-            ptDistance = 0;
-            absToCoord = [];
-            absTiCoord = [];
-            coOrdArray[2].forEach(function(item,index){
-                if(absToCoord[index] == null){
-                    absToCoord[index] = coOrdArray[0][index] + item;
-                    absTiCoord[index] = coOrdArray[1][index] + coOrdArray[3][index];
-                }
-                triCoord1 = coOrdArray[0][index] + (absToCoord[index] - coOrdArray[0][index])*perc;
-                triCoord2 = absToCoord[index] + (absTiCoord[index] - absToCoord[index])*perc;
-                triCoord3 = absTiCoord[index] + (coOrdArray[1][index] - absTiCoord[index])*perc;
-                liCoord1 = triCoord1 + (triCoord2 - triCoord1)*perc;
-                liCoord2 = triCoord2 + (triCoord3 - triCoord2)*perc;
-                ptCoord = liCoord1 + (liCoord2 - liCoord1)*perc;
-                point.push(ptCoord);
-                if(lastPoint !== null){
-                    ptDistance += Math.pow(point[index] - lastPoint[index],2);
-                }
-            });
-            ptDistance = Math.sqrt(ptDistance);
-            addedLength += ptDistance;
-            bezierData.points.push({partialLength: ptDistance, point: point});
-            lastPoint = point;
-        }
-        bezierData.segmentLength = addedLength;
-        storedBezierCurves[bezierName] = bezierData;
-        return bezierData;
-    }
-
-    function pointOnLine2D(x1,y1, x2,y2, x3,y3){
-        return Math.abs(((x2 - x1) * (y3 - y1)) - ((x3 - x1) * (y2 - y1))) < 0.0000001;
-    }
-
-    function buildBezierData(keyData){
-        var curveSegments = 1000, absToCoord, absTiCoord;
-        var k;
-        var triCoord1,triCoord2,triCoord3,liCoord1,liCoord2,ptCoord,perc,addedLength = 0;
-        var ptDistance;
-        var point,lastPoint = null;
-        var bezierData = {
-            points :[],
-            segmentLength: 0
-        };
-        if(pointOnLine2D(keyData.s[0],keyData.s[1],keyData.e[0],keyData.e[1],keyData.s[0]+keyData.to[0],keyData.s[1]+keyData.to[1])
-            && pointOnLine2D(keyData.s[0],keyData.s[1],keyData.e[0],keyData.e[1],keyData.e[0]+keyData.ti[0],keyData.e[1]+keyData.ti[1])){
-            curveSegments = 2;
-        }
-        for(k=0;k<curveSegments;k+=1){
-            point = [];
-            perc = k/(curveSegments-1);
-            ptDistance = 0;
-            absToCoord = [];
-            absTiCoord = [];
-            keyData.to.forEach(function(item,index){
-                if(absToCoord[index] == null){
-                    absToCoord[index] = keyData.s[index] + item;
-                    absTiCoord[index] = keyData.e[index] + keyData.ti[index];
-                }
-                triCoord1 = keyData.s[index] + (absToCoord[index] - keyData.s[index])*perc;
-                triCoord2 = absToCoord[index] + (absTiCoord[index] - absToCoord[index])*perc;
-                triCoord3 = absTiCoord[index] + (keyData.e[index] - absTiCoord[index])*perc;
-                liCoord1 = triCoord1 + (triCoord2 - triCoord1)*perc;
-                liCoord2 = triCoord2 + (triCoord3 - triCoord2)*perc;
-                ptCoord = liCoord1 + (liCoord2 - liCoord1)*perc;
-                point.push(ptCoord);
-                if(lastPoint !== null){
-                    ptDistance += Math.pow(point[index] - lastPoint[index],2);
-                }
-            });
-            ptDistance = Math.sqrt(ptDistance);
-            addedLength += ptDistance;
-            bezierData.points.push({partialLength: addedLength, point: point});
-            lastPoint = point;
-        }
-        keyData.bezierData = bezierData;
-        bezierData.segmentLength = addedLength;
-    }
-
     var keyData, nextKeyData,propertyArray,bezierData;
 
     function getInterpolatedValue(keyframes, frameNum, offsetTime){
@@ -355,10 +256,10 @@
         }
 
         if(keyData.to && !keyData.bezierData){
-            buildBezierData(keyData);
+            bez.buildBezierData(keyData);
         }
         var k, kLen;
-        var easingFnName, perc, j = 0;
+        var perc, j = 0;
         propertyArray = [];
         if(keyData.to){
             bezierData = keyData.bezierData;
@@ -367,18 +268,20 @@
             }else if(frameNum < keyData.t-offsetTime){
                 return bezierData.points[0].point;
             }
-            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));
+            perc = bez.getEasingCurve([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;
+            var addedLength = 0;
             while(j<bezierData.points.length){
+                addedLength +=bezierData.points[j].partialLength;
                 if(frameNum == 0 || distanceInLine == 0 || perc == 0){
                     propertyArray = bezierData.points[j].point;
                     break;
                 }else if(j == bezierData.points.length - 1){
                     propertyArray = bezierData.points[j].point;
-                }else if(distanceInLine > bezierData.points[j].partialLength && distanceInLine < bezierData.points[j+1].partialLength){
+                }else if(distanceInLine > addedLength && distanceInLine < addedLength + bezierData.points[j+1].partialLength){
                     kLen = bezierData.points[j].point.length;
-                    segmentPerc = (distanceInLine-bezierData.points[j].partialLength)/(bezierData.points[j+1].partialLength-bezierData.points[j].partialLength);
+                    segmentPerc = (distanceInLine-addedLength)/(bezierData.points[j+1].partialLength);
                     for(k=0;k<kLen;k+=1){
                         propertyArray.push(bezierData.points[j].point[k] + (bezierData.points[j+1].point[k] - bezierData.points[j].point[k])*segmentPerc);
                     }
@@ -403,7 +306,7 @@
                         inX = keyData.i.x;
                         inY = keyData.i.y;
                     }
-                    perc = bez.getCurve([outX,outY,inX,inY])('',(frameNum)-(keyData.t-offsetTime),0,1,(nextKeyData.t-offsetTime)-(keyData.t-offsetTime));
+                    perc = bez.getEasingCurve([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){
@@ -510,9 +413,6 @@
             trOb.s = scale instanceof Array ? scale.length > 1 ? [scale[0]/100,scale[1]/100,scale[2]/100] : [scale[0]/100,scale[0]/100,scale[0]/100] : [scale/100,scale/100,scale/100];
             trOb.r = rot instanceof Array ? rot.length > 1 ? [rot[0]*Math.PI/180,rot[1]*Math.PI/180,rot[2]*Math.PI/180] : [rot[0]*Math.PI/180,rot[0]*Math.PI/180,rot[0]*Math.PI/180] : [0,0,rot*Math.PI/180];
             trOb.p = pos;
-            if(subframeEnabled){
-                item.an = [];
-            }
             item.an[offsettedFrameNum] = {
                 forwardFrame : offsettedFrameNum,
                 tr: dataOb,
@@ -523,7 +423,7 @@
                 maskProps = item.masksProperties;
                 len = maskProps.length;
                 for(i=0;i<len;i+=1){
-                    if(!maskProps[i].pathStrings || subframeEnabled){
+                    if(!maskProps[i].pathStrings){
                         maskProps[i].pathStrings = [];
                         maskProps[i].pathVertices = [];
                         maskProps[i].opacity = [];
@@ -544,7 +444,7 @@
                 len = item.shapes.length;
                 for(i=0;i<len;i+=1){
                     shapeItem = item.shapes[i];
-                    if(!shapeItem._created || subframeEnabled){
+                    if(!shapeItem._created){
                         shapeItem.an.tr = [];
                         shapeItem.an.renderedFrame = {};
                         if(shapeItem.ks){
@@ -561,7 +461,7 @@
                             shapeItem.an.stroke = [];
                         }
                     }
-                    if(shapeItem.trim && (!shapeItem._created  || subframeEnabled)){
+                    if(shapeItem.trim && (!shapeItem._created)){
                         shapeItem.trim.an = [];
                     }
                     if(shapeItem.fl){
@@ -654,7 +554,6 @@
     var moduleOb = {};
     moduleOb.completeData = completeData;
     moduleOb.renderFrame = renderFrame;
-    moduleOb.drawBezierCurve = drawBezierCurve;
 
     return moduleOb;
 }());
\ No newline at end of file
diff --git a/player/js/utils/MatrixManager.js b/player/js/utils/MatrixManager.js
index a870542..9a57e1c 100644
--- a/player/js/utils/MatrixManager.js
+++ b/player/js/utils/MatrixManager.js
@@ -1,5 +1,7 @@
 var MatrixManager = function(){
 
+    var mat = new Matrix();
+
     var returnMatrix3D = function(rX, rY, rZ, scaleX, scaleY, scaleZ, tX, tY, tZ) {
 
         var rotationXMatrix, rotationYMatrix, rotationZMatrix, s, scaleMatrix, transformationMatrix, translationMatrix;
@@ -63,7 +65,7 @@
     };*/
 
     var returnMatrix2D = function(rX, scaleX, scaleY, tX, tY){
-        return new Matrix().translate(tX,tY).rotate(rX).scale(scaleX,scaleY).toCSS();
+        return mat.reset().translate(tX,tY).rotate(rX).scale(scaleX,scaleY).toCSS();
     };
 
     /*var returnMatrix2DArray = function(rX, scaleX, scaleY, tX, tY){
@@ -86,7 +88,7 @@
     };*/
 
     var returnMatrix2DArray = function(rX, scaleX, scaleY, tX, tY){
-        return new Matrix().translate(tX,tY).rotate(rX).scale(scaleX,scaleY).toArray();
+        return mat.reset().translate(tX,tY).rotate(rX).scale(scaleX,scaleY).toArray();
     };
 
     var get2DMatrix = function(animData){
diff --git a/player/js/utils/bez.js b/player/js/utils/bez.js
new file mode 100644
index 0000000..7d8f8e3
--- /dev/null
+++ b/player/js/utils/bez.js
@@ -0,0 +1,101 @@
+var bez = (function(){
+
+    var easingFunctions = {};
+    var storedBezierCurves = {};
+
+    function pointOnLine2D(x1,y1, x2,y2, x3,y3){
+        return Math.abs(((x2 - x1) * (y3 - y1)) - ((x3 - x1) * (y2 - y1))) < 0.0000001;
+    }
+
+    function getEasingCurve(encodedFuncName, coOrdArray) {
+        coOrdArray = encodedFuncName;
+        encodedFuncName = 'bez_' + coOrdArray.join('_').replace(/\./g, 'p');
+        if(easingFunctions[encodedFuncName]){
+            return easingFunctions[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]));
+                };
+            return 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 / (C[0] + x * (2 * B[0] + 3 * A[0] * x));
+                }
+                return bezCoOrd(x, 1);
+            }
+        };
+        easingFunctions[encodedFuncName] = function(x, t, b, c, d) {
+            return c * polyBez([coOrdArray[0], coOrdArray[1]], [coOrdArray[2], coOrdArray[3]])(t/d) + b;
+        };
+        return easingFunctions[encodedFuncName];
+    }
+
+    function drawBezierCurve(pt1,pt2,pt3,pt4){
+        var bezierName = (pt1.join('_')+'_'+pt2.join('_')+'_'+pt3.join('_')+'_'+pt4.join('_')).replace(/\./g, 'p');
+        if(storedBezierCurves[bezierName]){
+            return storedBezierCurves[bezierName];
+        }
+        var curveSegments = 500, absToCoord, absTiCoord;
+        var k;
+        var i, len;
+        var triCoord1,triCoord2,triCoord3,liCoord1,liCoord2,ptCoord,perc,addedLength = 0;
+        var ptDistance;
+        var point,lastPoint = null;
+        var bezierData = {
+            points :[],
+            segmentLength: 0
+        };
+        if(pointOnLine2D(pt1[0],pt1[1],pt2[0],pt2[1],pt1[0]+pt3[0],pt1[1]+pt3[1])
+            && pointOnLine2D(pt1[0],pt1[1],pt2[0],pt2[1],pt2[0]+pt4[0],pt2[1]+pt4[1])){
+            curveSegments = 2;
+        }
+        for(k=0;k<curveSegments;k+=1){
+            point = [];
+            perc = k/(curveSegments-1);
+            ptDistance = 0;
+            absToCoord = [];
+            absTiCoord = [];
+            len = pt3.length;
+            for(i=0;i<len;i+=1){
+                if(absToCoord[i] == null){
+                    absToCoord[i] = pt1[i] + pt3[i];
+                    absTiCoord[i] = pt2[i] + pt4[i];
+                }
+                triCoord1 = pt1[i] + (absToCoord[i] - pt1[i])*perc;
+                triCoord2 = absToCoord[i] + (absTiCoord[i] - absToCoord[i])*perc;
+                triCoord3 = absTiCoord[i] + (pt2[i] - absTiCoord[i])*perc;
+                liCoord1 = triCoord1 + (triCoord2 - triCoord1)*perc;
+                liCoord2 = triCoord2 + (triCoord3 - triCoord2)*perc;
+                ptCoord = liCoord1 + (liCoord2 - liCoord1)*perc;
+                point.push(ptCoord);
+                if(lastPoint !== null){
+                    ptDistance += Math.pow(point[i] - lastPoint[i],2);
+                }
+            }
+            ptDistance = Math.sqrt(ptDistance);
+            addedLength += ptDistance;
+            bezierData.points.push({partialLength: ptDistance, point: point});
+            lastPoint = point;
+        }
+        bezierData.segmentLength = addedLength;
+        storedBezierCurves[bezierName] = bezierData;
+        return bezierData;
+    }
+
+    function buildBezierData(keyData){
+        keyData.bezierData = drawBezierCurve(keyData.s,keyData.e,keyData.to,keyData.ti);
+    }
+
+    var ob = {
+        getEasingCurve : getEasingCurve,
+        drawBezierCurve : drawBezierCurve,
+        buildBezierData : buildBezierData
+    };
+
+    return ob;
+}());
\ No newline at end of file
diff --git a/player/js/utils/common.js b/player/js/utils/common.js
index bf7d32d..ad9bf7c 100644
--- a/player/js/utils/common.js
+++ b/player/js/utils/common.js
@@ -48,4 +48,9 @@
         g: parseInt(result[2], 16),
         b: parseInt(result[3], 16)
     } : null;
+}
+
+function fillToRgba(hex,alpha){
+    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+    return result ? 'rgba('+parseInt(result[1], 16)+','+parseInt(result[2], 16)+','+parseInt(result[3], 16)+','+ alpha+')' : null;
 }
\ No newline at end of file