added skpath class
diff --git a/player/index.html b/player/index.html
index f72474a..9056ae2 100644
--- a/player/index.html
+++ b/player/index.html
@@ -62,6 +62,7 @@
     <script src="js/utils/common.js"></script>
     <script src="js/utils/BaseEvent.js"></script>
     <script src="js/utils/helpers/arrays.js"></script>
+    <script src="js/utils/helpers/skpaths.js"></script>
     <script src="js/utils/helpers/svg_elements.js"></script>
     <script src="js/utils/helpers/html_elements.js"></script>
     <script src="js/utils/helpers/dynamicProperties.js"></script>
diff --git a/player/js/utils/helpers/skpaths.js b/player/js/utils/helpers/skpaths.js
new file mode 100644
index 0000000..a1eddf9
--- /dev/null
+++ b/player/js/utils/helpers/skpaths.js
@@ -0,0 +1,100 @@
+var skpaths_factory = (function() {
+	var ob = {
+		createFromCommands: createFromCommands
+	}
+
+	function createFromCommands(cmdArr) {
+		var typedArrayFrom2D = floatTypedArrayFrom2D(cmdArr);
+		var cmd = typedArrayFrom2D[0];
+		var len = typedArrayFrom2D[1];
+		var path = Module.FromCmds(cmd, len);
+		Module._free(cmd);
+		return new WASMPath(path);
+	}
+
+	function floatTypedArrayFrom2D(arr) {
+		// expects 2d array where index 0 is verb and index 1-n are args
+		var len = 0, cmd, c, ii, jj;
+		for (ii = 0; ii < arr.length; ii += 1) {
+		  len += arr[ii].length;
+		}
+
+		var ta = new Float32Array(len);
+		var i = 0;
+		for (ii = 0; ii < arr.length; ii += 1) {
+		  for (jj = 0; jj < arr[ii].length; jj += 1) {
+		    ta[i] = arr[ii][jj];
+		    i++;
+		  }
+		}
+
+		var retVal = Module._malloc(ta.length * ta.BYTES_PER_ELEMENT);
+		Module.HEAPF32.set(ta, retVal / ta.BYTES_PER_ELEMENT);
+		return [retVal, len];
+	}
+
+	return ob
+}())
+
+function WASMPath(path) {
+	this.path = path;
+}
+
+WASMPath.prototype = {
+	stroke: function(amount, joinType, strokeCap) {
+		var path = this.path.stroke(amount, joinType, strokeCap);
+		return new WASMPath(path);
+	},
+
+	strokeAndReplace: function(amount, joinType, strokeCap) {
+		var wasmPath = this.stroke(amount, joinType, strokeCap);
+		this.destroy();
+		return wasmPath;
+	},
+
+	simplify: function(originalPath) {
+		var path = this.path.simplify();
+		return new WASMPath(path);
+	},
+
+
+	simplifyAndReplace: function() {
+		var wasmPath = this.simplify();
+		this.destroy();
+		return wasmPath;
+	},
+
+	op: function(operation, operatingPath) {
+		var path = this.path.op(operatingPath.path, operation);
+		return new WASMPath(path);
+	},
+
+	opAndReplace: function(operation, operatingPath) {
+		if(!operatingPath) {
+			operatingPath = this;
+		}
+		var wasmPath = this.op(operation, operatingPath);
+		this.destroy();
+		operatingPath.destroy();
+		return wasmPath;
+	},
+
+	destroy: function() {
+		if(this.path) {
+			this.path.delete();
+			this.path = null;
+		}
+	},
+
+	setFillType: function(fill_type) {
+		this.path.setFillType(fill_type);
+		return this;
+	},
+
+	toCmds: function() {
+		if(this.path) {
+			return this.path.toCmds();
+		}
+		return this;
+	}
+}
\ No newline at end of file
diff --git a/player/js/utils/shapes/MergePathModifier.js b/player/js/utils/shapes/MergePathModifier.js
index 93671ce..831c31a 100644
--- a/player/js/utils/shapes/MergePathModifier.js
+++ b/player/js/utils/shapes/MergePathModifier.js
@@ -96,7 +96,7 @@
 	return paper_path
 }
 
-MergePathModifier.prototype.floatTypedArrayFrom2D = function(arr) {
+/*MergePathModifier.prototype.floatTypedArrayFrom2D = function(arr) {
 	// expects 2d array where index 0 is verb and index 1-n are args
 	var len = 0, cmd, c, ii, jj;
 	for (ii = 0; ii < arr.length; ii += 1) {
@@ -115,7 +115,7 @@
 	var retVal = Module._malloc(ta.length * ta.BYTES_PER_ELEMENT);
 	Module.HEAPF32.set(ta, retVal / ta.BYTES_PER_ELEMENT);
 	return [retVal, len];
-}
+}*/
 
 MergePathModifier.prototype.SkPathFromCmdTyped = function(cmdArr) {
 	var typedArrayFrom2D = this.floatTypedArrayFrom2D(cmdArr);
diff --git a/player/js/utils/shapes/OffsetPathModifier.js b/player/js/utils/shapes/OffsetPathModifier.js
index 44650d4..74b29a3 100644
--- a/player/js/utils/shapes/OffsetPathModifier.js
+++ b/player/js/utils/shapes/OffsetPathModifier.js
@@ -4,6 +4,7 @@
 
 OffsetPathModifier.prototype.initModifierProperties = function(elem, data) {
     this.amount = PropertyFactory.getProp(elem, data.a, 0, null, this);
+    this.lineJoin = data.lj;
     this.getValue = this.processKeys;
 };
 
@@ -69,7 +70,7 @@
 		return;
     }
 	var commands = [];
-	var skPath, offsettedSkPath, outerSkPath, finalSkPath;
+	var skPath, offsettedSkPath;
 
 	for(i = 0; i < len; i += 1) {
 		shapeData = this.shapes[i];
@@ -77,31 +78,39 @@
 			shape = shapeData.shape;
 			commands.length = 0;
 			this.addShapeToCommands(shape, shapeData.data.transformers, shapeData.data.lvl, commands);
-			// commands = [[0,50,-50]];
-			// commands.push([1,50,50]);
-			// commands.push([1,-50,50]);
-			// commands.push([1,-50,-50]);
-			// commands.push([5]);
-			console.log(commands)
-			skPath = this.SkPathFromCmdTyped(commands);
-			offsettedSkPath = skPath.stroke(amount * 2, Module.StrokeJoin.MITER, Module.StrokeCap.BUTT);
-			if(commands[commands.length - 1][0] === 5) {
-				outerSkPath = offsettedSkPath.op(skPath, Module.PathOp.DIFFERENCE); // subtract the original path out
-				offsettedSkPath.delete();
-				// outerSkPath = offsettedSkPath;
-				// finalSkPath = outerSkPath.simplify();
-				// outerSkPath.delete();
-				finalSkPath = outerSkPath;
-			} else {
-				finalSkPath = offsettedSkPath.simplify();
-				offsettedSkPath.delete();
-				// finalSkPath = offsettedSkPath;
+
+			skPath = skpaths_factory.createFromCommands(commands);
+			if(amount < 0) {
+				skPath = skPath.opAndReplace(Module.PathOp.UNION);
+				// skPath = skpaths_factory.replaceFromBooleanOperation(skPath, skPath, Module.PathOp.UNION);
 			}
-		  	commands = finalSkPath.toCmds();
-		  	console.log(commands)
+			var joinType, strokeCap;
+			switch(this.lineJoin) {
+				case 2:
+				joinType = Module.StrokeJoin.ROUND;
+				strokeCap = Module.StrokeCap.ROUND;
+				break;
+				default: 
+				joinType = Module.StrokeJoin.MITER;
+				strokeCap = Module.StrokeCap.BUTT;
+				break;
+			}
+			if(commands[commands.length - 1][0] === 5) {
+				offsettedSkPath = skPath.stroke(Math.abs(amount) * 2, joinType, strokeCap);
+				let operation = amount < 0 ? Module.PathOp.REVERSE_DIFFERENCE : Module.PathOp.UNION;
+				skPath = offsettedSkPath
+					.opAndReplace(operation, skPath)
+					.simplifyAndReplace();
+			} else {
+				skPath = skPath
+				.strokeAndReplace(Math.abs(amount) * 2, joinType, strokeCap)
+				// .opAndReplace(Module.PathOp.UNION)
+				.setFillType(Module.FillType.EVENODD)
+				.simplifyAndReplace();
+			}
+		  	commands = skPath.toCmds();
 		  	// commands.length = 44;
-			skPath.delete();
-			finalSkPath.delete();
+			//skPath.destroy();
 	        localShapeCollection = shapeData.localShapeCollection;
 	        localShapeCollection.releaseShapes();
 	        localShapeCollection = this.createPathFromCommands(commands, localShapeCollection);
@@ -115,36 +124,6 @@
     }
 };
 
-OffsetPathModifier.prototype.floatTypedArrayFrom2D = function(arr) {
-	// expects 2d array where index 0 is verb and index 1-n are args
-	var len = 0, cmd, c, ii, jj;
-	for (ii = 0; ii < arr.length; ii += 1) {
-	  len += arr[ii].length;
-	}
-
-	var ta = new Float32Array(len);
-	var i = 0;
-	for (ii = 0; ii < arr.length; ii += 1) {
-	  for (jj = 0; jj < arr[ii].length; jj += 1) {
-	    ta[i] = arr[ii][jj];
-	    i++;
-	  }
-	}
-
-	var retVal = Module._malloc(ta.length * ta.BYTES_PER_ELEMENT);
-	Module.HEAPF32.set(ta, retVal / ta.BYTES_PER_ELEMENT);
-	return [retVal, len];
-}
-
-OffsetPathModifier.prototype.SkPathFromCmdTyped = function(cmdArr) {
-	var typedArrayFrom2D = this.floatTypedArrayFrom2D(cmdArr);
-	var cmd = typedArrayFrom2D[0];
-	var len = typedArrayFrom2D[1];
-	var path = Module.FromCmds(cmd, len);
-	Module._free(cmd);
-	return path;
-}
-
 OffsetPathModifier.prototype.createPathFromCommands = function(commands, localShapeCollection) {
 
 	var i, len;