merge with master
diff --git a/player/index.html b/player/index.html
index 9a8a2b2..0bc6b78 100644
--- a/player/index.html
+++ b/player/index.html
@@ -140,6 +140,7 @@
     <script src="js/utils/expressions/EffectInterface.js" data-light-skip="true"></script>
     <script src="js/utils/expressions/MaskInterface.js" data-light-skip="true"></script>
     <script src="js/utils/expressions/ExpressionValue.js" data-light-skip="true"></script>
+    <script src="js/utils/expressions/ExpressionValueFactory.js" data-light-skip="true"></script>
     <script src="js/effects/SliderEffect.js" data-light-skip="true"></script>
     <script src="js/effects/EffectsManagerPlaceholder.js" ></script>
     <script src="js/EffectsManager.js" data-light-skip="true"></script>
@@ -160,7 +161,7 @@
         container: elem,
         renderer: 'svg',
         loop: true,
-        autoplay: true,
+        autoplay: false,
         rendererSettings: {
             progressiveLoad:true,
             preserveAspectRatio: 'xMidYMid meet',
@@ -169,7 +170,7 @@
         path: 'exports/render/data.json'
     };
     anim = lottie.loadAnimation(animData);
-    anim.setSubframe(false);
+    anim.setSubframe(true);
 
 </script>
 </body>
diff --git a/player/js/utils/expressions/EffectInterface.js b/player/js/utils/expressions/EffectInterface.js
index b2b8f7b..3e51636 100644
--- a/player/js/utils/expressions/EffectInterface.js
+++ b/player/js/utils/expressions/EffectInterface.js
@@ -78,11 +78,17 @@
     }
 
     function createValueInterface(element, type, elem, propertyGroup){
+        var expressionProperty
+        if (element.p.propType === 'unidimensional') {
+            expressionProperty = ExpressionUnidimensionalValueFactory(element.p, 1);
+        } else if (element.p.propType === 'multidimensional') {
+            expressionProperty = ExpressionMultidimensionalValueFactory(element.p, 1);
+        }
         function interfaceFunction(){
             if(type === 10){
                 return elem.comp.compInterface(element.p.v);
             }
-            return ExpressionValue(element.p);
+            return expressionProperty();
         }
 
         if(element.p.setGroupProperty) {
diff --git a/player/js/utils/expressions/ExpressionManager.js b/player/js/utils/expressions/ExpressionManager.js
index 97cf819..8c3d140 100644
--- a/player/js/utils/expressions/ExpressionManager.js
+++ b/player/js/utils/expressions/ExpressionManager.js
@@ -574,7 +574,9 @@
         var hasParent = !!(elem.hierarchy && elem.hierarchy.length);
         var parent;
         var randSeed = Math.floor(Math.random()*1000000);
+        var globalData = elem.globalData;
         function executeExpression(_value) {
+            // globalData.pushExpression();
             value = _value;
             if (_needsRandom) {
                 seedRandom(randSeed);
@@ -631,6 +633,7 @@
             if (scoped_bm_rt.propType === "shape") {
                 scoped_bm_rt = shape_pool.clone(scoped_bm_rt.v);
             }
+            // globalData.popExpression();
             return scoped_bm_rt;
         }
         return executeExpression;
diff --git a/player/js/utils/expressions/ExpressionValue.js b/player/js/utils/expressions/ExpressionValue.js
index e025bc9..60ccda9 100644
--- a/player/js/utils/expressions/ExpressionValue.js
+++ b/player/js/utils/expressions/ExpressionValue.js
@@ -1,53 +1,51 @@
-var ExpressionValue = (function() {
-	return function(elementProp, mult, type) {
-        mult = mult || 1;
-        var expressionValue, arrayValue;
+function ExpressionValue(elementProp, mult, type) {
+    mult = mult || 1;
+    var expressionValue, arrayValue;
 
-		if (elementProp.k) {
-            elementProp.getValue();
-        }
-        var i, len, arrValue, val;
-        if (type) {
-        	if(type === 'color') {
-        		len = 4;
-                expressionValue = createTypedArray('float32', len);
-                arrValue = createTypedArray('float32', len);
-		        for (i = 0; i < len; i += 1) {
-		            expressionValue[i] = arrValue[i] = (i < 3) ? elementProp.v[i] * mult : 1;
-		        }
-	        	expressionValue.value = arrValue;
-        	}
-        } else if (elementProp.propType === 'unidimensional'){
-            val = elementProp.v * mult;
-            expressionValue = new Number(val);
-            expressionValue.value = val;
-        } else {
-        	len = elementProp.pv.length;
+	if (elementProp.k) {
+        elementProp.getValue();
+    }
+    var i, len, arrValue, val;
+    if (type) {
+    	if(type === 'color') {
+    		len = 4;
             expressionValue = createTypedArray('float32', len);
             arrValue = createTypedArray('float32', len);
 	        for (i = 0; i < len; i += 1) {
-	            expressionValue[i] = arrValue[i] = elementProp.v[i] * mult;
+	            expressionValue[i] = arrValue[i] = (i < 3) ? elementProp.v[i] * mult : 1;
 	        }
-	        expressionValue.value = arrValue;
+        	expressionValue.value = arrValue;
+    	}
+    } else if (elementProp.propType === 'unidimensional'){
+        val = elementProp.v * mult;
+        expressionValue = new Number(val);
+        expressionValue.value = val;
+    } else {
+    	len = elementProp.pv.length;
+        expressionValue = createTypedArray('float32', len);
+        arrValue = createTypedArray('float32', len);
+        for (i = 0; i < len; i += 1) {
+            expressionValue[i] = arrValue[i] = elementProp.v[i] * mult;
         }
-        
-        expressionValue.numKeys = elementProp.keyframes ? elementProp.keyframes.length : 0;
-        expressionValue.key = function(pos) {
-            if (!expressionValue.numKeys) {
-                return 0;
-            } else {
-                return elementProp.keyframes[pos-1].t;
-            }
-        };
-        expressionValue.valueAtTime = elementProp.getValueAtTime;
-        expressionValue.speedAtTime = elementProp.getSpeedAtTime;
-        expressionValue.velocityAtTime = elementProp.getVelocityAtTime;
-        expressionValue.propertyGroup = elementProp.propertyGroup;
-        Object.defineProperty(expressionValue, 'velocity', {
-            get: function(){
-                return elementProp.getVelocityAtTime(elementProp.comp.currentFrame);
-            }
-        });
-        return expressionValue;
-	};
-}());
\ No newline at end of file
+        expressionValue.value = arrValue;
+    }
+    
+    expressionValue.numKeys = elementProp.keyframes ? elementProp.keyframes.length : 0;
+    expressionValue.key = function(pos) {
+        if (!expressionValue.numKeys) {
+            return 0;
+        } else {
+            return elementProp.keyframes[pos-1].t;
+        }
+    };
+    expressionValue.valueAtTime = elementProp.getValueAtTime;
+    expressionValue.speedAtTime = elementProp.getSpeedAtTime;
+    expressionValue.velocityAtTime = elementProp.getVelocityAtTime;
+    expressionValue.propertyGroup = elementProp.propertyGroup;
+    Object.defineProperty(expressionValue, 'velocity', {
+        get: function(){
+            return elementProp.getVelocityAtTime(elementProp.comp.currentFrame);
+        }
+    });
+    return expressionValue;
+};
\ No newline at end of file
diff --git a/player/js/utils/expressions/ExpressionValueFactory.js b/player/js/utils/expressions/ExpressionValueFactory.js
new file mode 100644
index 0000000..3ad0d27
--- /dev/null
+++ b/player/js/utils/expressions/ExpressionValueFactory.js
@@ -0,0 +1,63 @@
+var ExpressionMultidimensionalValueFactory = function(property, mult) {
+	if(!property) {
+		property = {pv:[0,0,0], v:[0,0,0]}
+	}
+	var len = property.pv.length;
+    var expressionValue = createTypedArray('float32', len);
+    var arrValue = createTypedArray('float32', len);
+    expressionValue.value = arrValue;
+    completeProperty(expressionValue, property);
+
+    return function() {
+    	if (property.k) {
+	        property.getValue();
+	    }
+	    for (i = 0; i < len; i += 1) {
+            expressionValue[i] = arrValue[i] = property.v[i] * mult;
+        }
+        return expressionValue;
+    }
+}
+
+var ExpressionUnidimensionalValueFactory = function(property, mult) {
+	if(!property) {
+		property = {pv:0, v:0}
+	}
+	var val = property.pv * mult;
+    var expressionValue = new Number(val);
+    expressionValue.value = val;
+	completeProperty(expressionValue, property);
+
+    return function() {
+    	if (property.k) {
+	        property.getValue();
+	    }
+	    val = property.v * mult;
+	    if(expressionValue.value !== val) {
+	        expressionValue = new Number(val);
+	        expressionValue.value = val;
+	        completeProperty(expressionValue, property);
+	    }
+        return expressionValue;
+    }
+}
+
+function completeProperty(expressionValue, property) {
+    Object.defineProperty(expressionValue, 'velocity', {
+        get: function(){
+            return property.getVelocityAtTime(property.comp.currentFrame);
+        }
+    });
+    expressionValue.numKeys = property.keyframes ? property.keyframes.length : 0;
+    expressionValue.key = function(pos) {
+        if (!expressionValue.numKeys) {
+            return 0;
+        } else {
+            return property.keyframes[pos-1].t;
+        }
+    };
+    expressionValue.valueAtTime = property.getValueAtTime;
+    expressionValue.speedAtTime = property.getSpeedAtTime;
+    expressionValue.velocityAtTime = property.getVelocityAtTime;
+    expressionValue.propertyGroup = property.propertyGroup;
+}
\ No newline at end of file
diff --git a/player/js/utils/expressions/Expressions.js b/player/js/utils/expressions/Expressions.js
index 7932a1d..24a6012 100644
--- a/player/js/utils/expressions/Expressions.js
+++ b/player/js/utils/expressions/Expressions.js
@@ -4,8 +4,40 @@
 
 
     function initExpressions(animation){
+
+    	var stackCount = 0;
+    	var registers = [];
+
+    	function pushExpression() {
+			stackCount += 1;
+    	}
+
+    	function popExpression() {
+			stackCount -= 1;
+			if (stackCount === 0) {
+				releaseInstances();
+			}
+    	}
+
+    	function registerExpressionProperty(expression) {
+    		if (registers.indexOf(expression) === -1) {
+				registers.push(expression)
+    		}
+    	}
+
+    	function releaseInstances() {
+    		var i, len = registers.length;
+    		for (i = 0; i < len; i += 1) {
+				registers[i].release();
+    		}
+    		registers.length = 0;
+    	}
+
         animation.renderer.compInterface = CompExpressionInterface(animation.renderer);
         animation.renderer.globalData.projectInterface.registerComposition(animation.renderer);
+        animation.renderer.globalData.pushExpression = pushExpression;
+        animation.renderer.globalData.popExpression = popExpression;
+        animation.renderer.globalData.registerExpressionProperty = registerExpressionProperty;
     }
    return ob;
 }());
diff --git a/player/js/utils/expressions/ShapeInterface.js b/player/js/utils/expressions/ShapeInterface.js
index 60ff03e..ae67590 100644
--- a/player/js/utils/expressions/ShapeInterface.js
+++ b/player/js/utils/expressions/ShapeInterface.js
@@ -109,13 +109,12 @@
         Object.defineProperties(interfaceFunction, {
             'color': {
                 get: function() {
+                    //TODO fix color
                     return ExpressionValue(view.c, 1 / view.c.mult, 'color');
                 }
             },
             'opacity': {
-                get: function() {
-                    return ExpressionValue(view.o, 100);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.o, 100)
             },
             '_name': { value: shape.nm },
             'mn': { value: shape.mn }
@@ -143,9 +142,7 @@
         }
         function addPropertyToDashOb(i) {
             Object.defineProperty(dashOb, shape.d[i].nm, {
-                get: function(){
-                    return ExpressionValue(view.d.dataProps[i].p);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.d.dataProps[i].p, 1)
             });
         }
         var i, len = shape.d ? shape.d.length : 0;
@@ -164,21 +161,20 @@
                 return interfaceFunction.strokeWidth;
             }
         }
+        var _color = ExpressionMultidimensionalValueFactory(view.c, 1 / view.c.mult);
         Object.defineProperties(interfaceFunction, {
             'color': {
                 get: function() {
-                    return ExpressionValue(view.c, 1 / view.c.mult, 'color');
+                    return _color();
+                    //TODO fix color
+                    // return ExpressionValue(view.c, 1 / view.c.mult, 'color');
                 }
             },
             'opacity': {
-                get: function() {
-                    return ExpressionValue(view.o, 100);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.o, 100)
             },
             'strokeWidth': {
-                get: function() {
-                    return ExpressionValue(view.w);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.w, 1)
             },
             'dash': {
                 get: function() {
@@ -224,19 +220,13 @@
 
         Object.defineProperties(interfaceFunction, {
             'start': {
-                get: function() {
-                    return ExpressionValue(view.s, 1 / view.s.mult);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.s, 1 / view.s.mult)
             },
             'end': {
-                get: function() {
-                    return ExpressionValue(view.e, 1 / view.e.mult);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.e, 1 / view.e.mult)
             },
             'offset': {
-                get: function() {
-                    return ExpressionValue(view.o);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.o, 1)
             },
             '_name': { value: shape.nm }
         });
@@ -289,39 +279,25 @@
         }
         Object.defineProperties(interfaceFunction, {
             'opacity': {
-                get: function(){
-                    return ExpressionValue(view.transform.mProps.o, 1/view.transform.mProps.o.mult);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.transform.mProps.o, 1 / view.transform.mProps.o.mult)
             },
             'position': {
-                get: function(){
-                    return ExpressionValue(view.transform.mProps.p);
-                }
+                get: ExpressionMultidimensionalValueFactory(view.transform.mProps.p, 1)
             },
             'anchorPoint': {
-                get: function(){
-                    return ExpressionValue(view.transform.mProps.a);
-                }
+                get: ExpressionMultidimensionalValueFactory(view.transform.mProps.a, 1)
             },
             'scale': {
-                get: function(){
-                    return ExpressionValue(view.transform.mProps.s, 1 / view.transform.mProps.s.mult);
-                }
+                get: ExpressionMultidimensionalValueFactory(view.transform.mProps.s, 1 / view.transform.mProps.s.mult)
             },
             'rotation': {
-                get: function(){
-                    return ExpressionValue(view.transform.mProps.r, 1 / view.transform.mProps.r.mult);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.transform.mProps.r, 1 / view.transform.mProps.r.mult)
             },
             'skew': {
-                get: function(){
-                    return ExpressionValue(view.transform.mProps.sk);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.transform.mProps.sk, 1)
             },
             'skewAxis': {
-                get: function(){
-                    return ExpressionValue(view.transform.mProps.sa);
-                }
+                get: ExpressionUnidimensionalValueFactory(view.transform.mProps.sa, 1)
             },
             '_name': { value: shape.nm }
         });
@@ -350,16 +326,13 @@
                 return interfaceFunction.size;
             }
         }
+
         Object.defineProperties(interfaceFunction, {
             'size': {
-                get: function(){
-                    return ExpressionValue(prop.s);
-                }
+                get: ExpressionMultidimensionalValueFactory(prop.s, 1)
             },
             'position': {
-                get: function(){
-                    return ExpressionValue(prop.p);
-                }
+                get: ExpressionMultidimensionalValueFactory(prop.p, 1)
             },
             '_name': { value: shape.nm }
         });
@@ -411,47 +384,28 @@
             }
 
         }
+
         Object.defineProperties(interfaceFunction, {
             'position': {
-                get: function() {
-                    return ExpressionValue(prop.p);
-                }
+                get: ExpressionMultidimensionalValueFactory(prop.p, 1)
             },
             'rotation': {
-                get: function() {
-                    return ExpressionValue(prop.r, 1 / prop.r.mult);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.r, 1 / prop.r.mult)
             },
             'points': {
-                get: function() {
-                    return ExpressionValue(prop.pt);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.pt, 1)
             },
             'outerRadius': {
-                get: function() {
-                    return ExpressionValue(prop.or);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.or, 1)
             },
             'outerRoundness': {
-                get: function(){
-                    return ExpressionValue(prop.os);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.os, 1)
             },
             'innerRadius': {
-                get: function(){
-                    if(!prop.ir){
-                        return 0;
-                    }
-                    return ExpressionValue(prop.ir);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.ir, 1)
             },
             'innerRoundness': {
-                get: function(){
-                    if(!prop.is){
-                        return 0;
-                    }
-                    return ExpressionValue(prop.is, 1 / prop.is.mult);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.is, 1 / prop.is.mult)
             },
             '_name': { value: shape.nm }
         });
@@ -487,19 +441,13 @@
         }
         Object.defineProperties(interfaceFunction, {
             'position': {
-                get: function(){
-                    return ExpressionValue(prop.p);
-                }
+                get: ExpressionMultidimensionalValueFactory(prop.p, 1)
             },
             'roundness': {
-                get: function(){
-                    return ExpressionValue(prop.r);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.r, 1)
             },
             'size': {
-                get: function(){
-                    return ExpressionValue(prop.s);
-                }
+                get: ExpressionMultidimensionalValueFactory(prop.s, 1)
             },
             '_name': { value: shape.nm }
         });
@@ -527,9 +475,7 @@
         }
         Object.defineProperties(interfaceFunction, {
             'radius': {
-                get: function() {
-                    return ExpressionValue(prop.rd);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.rd, 1)
             },
             '_name': { value: shape.nm }
         });
@@ -560,14 +506,10 @@
         }
         Object.defineProperties(interfaceFunction, {
             'copies': {
-                get: function(){
-                    return ExpressionValue(prop.c);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.c, 1)
             },
             'offset': {
-                get: function(){
-                    return ExpressionValue(prop.o);
-                }
+                get: ExpressionUnidimensionalValueFactory(prop.o, 1)
             },
             '_name': { value: shape.nm }
         });
diff --git a/player/js/utils/expressions/TransformInterface.js b/player/js/utils/expressions/TransformInterface.js
index 6643199..05edfa1 100644
--- a/player/js/utils/expressions/TransformInterface.js
+++ b/player/js/utils/expressions/TransformInterface.js
@@ -42,36 +42,27 @@
         }
 
         Object.defineProperty(_thisFunction, "rotation", {
-            get: function(){
-                if(transform.r) {
-                    return ExpressionValue(transform.r, 1/degToRads);
-                } else {
-                    return ExpressionValue(transform.rz, 1/degToRads);
-                }
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.r || transform.rz, 1 / degToRads)
         });
 
         Object.defineProperty(_thisFunction, "xRotation", {
-            get: function(){
-                    return ExpressionValue(transform.rx, 1/degToRads);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.rx, 1 / degToRads)
         });
 
         Object.defineProperty(_thisFunction, "yRotation", {
-            get: function(){
-                    return ExpressionValue(transform.ry, 1/degToRads);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.ry, 1 / degToRads)
         });
         Object.defineProperty(_thisFunction, "scale", {
-            get: function () {
-                return ExpressionValue(transform.s, 100);
-            }
+            get: ExpressionMultidimensionalValueFactory(transform.s, 100)
         });
 
+        if(transform.p) {
+            var _transformFactory = ExpressionMultidimensionalValueFactory(transform.p, 1);
+        }
         Object.defineProperty(_thisFunction, "position", {
             get: function () {
                 if(transform.p) {
-                    return ExpressionValue(transform.p);
+                    return _transformFactory();
                 } else {
                     return [transform.px.v, transform.py.v, transform.pz ? transform.pz.v : 0];
                 }
@@ -79,51 +70,39 @@
         });
 
         Object.defineProperty(_thisFunction, "xPosition", {
-            get: function () {
-                return ExpressionValue(transform.px);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.px, 1)
         });
 
         Object.defineProperty(_thisFunction, "yPosition", {
-            get: function () {
-                return ExpressionValue(transform.py);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.py, 1)
         });
 
         Object.defineProperty(_thisFunction, "zPosition", {
-            get: function () {
-                return ExpressionValue(transform.pz);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.pz, 1)
         });
 
         Object.defineProperty(_thisFunction, "anchorPoint", {
-            get: function () {
-                return ExpressionValue(transform.a);
-            }
+            get: ExpressionMultidimensionalValueFactory(transform.a, 1)
         });
 
         Object.defineProperty(_thisFunction, "opacity", {
-            get: function () {
-                return ExpressionValue(transform.o, 100);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.o, 100)
         });
 
         Object.defineProperty(_thisFunction, "skew", {
-            get: function () {
-                return ExpressionValue(transform.sk);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.sk, 1)
         });
 
         Object.defineProperty(_thisFunction, "skewAxis", {
-            get: function () {
-                return ExpressionValue(transform.sa);
-            }
+            get: ExpressionUnidimensionalValueFactory(transform.sa, 1)
         });
 
         Object.defineProperty(_thisFunction, "orientation", {
-            get: function () {
+            get: ExpressionMultidimensionalValueFactory(transform.or, 1)
+            //TODO: check if multi or unidimensional
+            /*get: function () {
                 return ExpressionValue(transform.or);
-            }
+            }*/
         });
 
         return _thisFunction;