build process
diff --git a/package.json b/package.json
index a3c0d50..f6debd1 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "url": "https://github.com/airbnb/lottie-web.git"
   },
   "scripts": {
-    "build": "npx eslint ./player/js/**/* && node tasks/build.js",
+    "build": "npx eslint ./player/js/**/* && rollup -c && node tasks/build_worker",
     "build_test": "node tasks/build.js",
     "lint": "npx eslint ./player/js/**/*",
     "lint:fix": "npx eslint ./player/js/**/* --fix"
diff --git a/player/js/worker_wrapper.js b/player/js/worker_wrapper.js
new file mode 100644
index 0000000..598289b
--- /dev/null
+++ b/player/js/worker_wrapper.js
@@ -0,0 +1,629 @@
+function workerContent() {
+  var localIdCounter = 0;
+  var animations = {};
+
+  var styleProperties = ['width', 'height', 'display', 'transform', 'opacity', 'contentVisibility'];
+  function createElement(namespace, type) {
+    var style = {
+      serialize: function () {
+        var obj = {};
+        for (var i = 0; i < styleProperties.length; i += 1) {
+          var propertyKey = styleProperties[i];
+          var keyName = '_' + propertyKey;
+          if (keyName in this) {
+            obj[propertyKey] = this[keyName];
+          }
+        }
+        return obj;
+      },
+    };
+    styleProperties.forEach(function (propertyKey) {
+      Object.defineProperty(style, propertyKey, {
+        set: function (value) {
+          if (!element._isDirty) {
+            element._isDirty = true;
+          }
+          element._changedStyles.push(propertyKey);
+          var keyName = '_' + propertyKey;
+          this[keyName] = value;
+        },
+        get: function () {
+          var keyName = '_' + propertyKey;
+          return this[keyName];
+        },
+      });
+    });
+    localIdCounter += 1;
+    var element = {
+      _state: 'init',
+      _isDirty: false,
+      _changedStyles: [],
+      _changedAttributes: [],
+      _changedElements: [],
+      type: type,
+      namespace: namespace,
+      children: [],
+      attributes: {
+        id: 'l_d_' + localIdCounter,
+      },
+      style: style,
+      appendChild: function (child) {
+        child.parentNode = this;
+        this.children.push(child);
+        this._isDirty = true;
+        this._changedElements.push([child, this.attributes.id]);
+      },
+      insertBefore: function (newElement, nextElement) {
+        var children = this.children;
+        for (var i = 0; i < children.length; i += 1) {
+          if (children[i] === nextElement) {
+            children.splice(i, 0, newElement);
+            this._isDirty = true;
+            this._changedElements.push([newElement, this.attributes.id, nextElement.attributes.id]);
+            return;
+          }
+        }
+        children.push(nextElement);
+      },
+      setAttribute: function (attribute, value) {
+        this.attributes[attribute] = value;
+        if (!element._isDirty) {
+          element._isDirty = true;
+        }
+        element._changedAttributes.push(attribute);
+      },
+      serialize: function () {
+        return {
+          type: this.type,
+          namespace: this.namespace,
+          style: this.style.serialize(),
+          attributes: this.attributes,
+          children: this.children.map(function (child) { return child.serialize(); }),
+        };
+      },
+      getContext: function () { return { fillRect: function () {} }; },
+      addEventListener: function (_, callback) {
+        setTimeout(callback, 1);
+      },
+      setAttributeNS: function (_, attribute, value) {
+        this.attributes[attribute] = value;
+        if (!element._isDirty) {
+          element._isDirty = true;
+        }
+        element._changedAttributes.push(attribute);
+      },
+    };
+    element.style = style;
+    return element;
+  }
+
+  var window = self; // eslint-disable-line no-redeclare, no-unused-vars
+
+  var document = { // eslint-disable-line no-redeclare
+    createElementNS: function (namespace, type) {
+      return createElement(namespace, type);
+    },
+    createElement: function (type) {
+      return createElement('', type);
+    },
+    getElementsByTagName: function () {
+      return [];
+    },
+  };
+  /* eslint-enable */
+  var lottieInternal = (function () {
+    'use strict';
+
+    /* <%= contents %> */
+
+    function addElementToList(element, list) {
+      list.push(element);
+      element._isDirty = false;
+      element._changedStyles.length = 0;
+      element._changedAttributes.length = 0;
+      element._changedElements.length = 0;
+      element.children.forEach(function (child) {
+        addElementToList(child, list);
+      });
+    }
+
+    function addChangedAttributes(element) {
+      var changedAttributes = element._changedAttributes;
+      var attributes = [];
+      var attribute;
+      for (var i = 0; i < changedAttributes.length; i += 1) {
+        attribute = changedAttributes[i];
+        attributes.push([attribute, element.attributes[attribute]]);
+      }
+      return attributes;
+    }
+
+    function addChangedStyles(element) {
+      var changedStyles = element._changedStyles;
+      var styles = [];
+      var style;
+      for (var i = 0; i < changedStyles.length; i += 1) {
+        style = changedStyles[i];
+        styles.push([style, element.style[style]]);
+      }
+      return styles;
+    }
+
+    function addChangedElements(element, elements) {
+      var changedElements = element._changedElements;
+      var elementsList = [];
+      var elementData;
+      for (var i = 0; i < changedElements.length; i += 1) {
+        elementData = changedElements[i];
+        elementsList.push([elementData[0].serialize(), elementData[1], elementData[2]]);
+        addElementToList(elementData[0], elements);
+      }
+      return elementsList;
+    }
+
+    function loadAnimation(payload) {
+      var params = payload.params;
+      var wrapper;
+      var animation;
+      var elements = [];
+      if (params.renderer === 'svg') {
+        wrapper = document.createElement('div');
+        params.container = wrapper;
+      } else {
+        var canvas = params.rendererSettings.canvas;
+        var ctx = canvas.getContext('2d');
+        params.rendererSettings.context = ctx;
+      }
+      animation = lottie.loadAnimation(params);
+      animation.addEventListener('error', function (error) {
+        console.log(error); // eslint-disable-line
+      });
+      animation.onError = function (error) {
+        console.log('ERRORO', error); // eslint-disable-line
+      };
+      if (params.renderer === 'svg') {
+        animation.addEventListener('DOMLoaded', function () {
+          var serialized = wrapper.serialize();
+          addElementToList(wrapper, elements);
+          self.postMessage({
+            type: 'loaded',
+            payload: {
+              id: payload.id,
+              tree: serialized.children[0],
+              totalFrames: animation.totalFrames,
+              frameRate: animation.frameRate,
+            },
+          });
+        });
+        animation.addEventListener('drawnFrame', function (event) {
+          var changedElements = [];
+          var element;
+          for (var i = 0; i < elements.length; i += 1) {
+            element = elements[i];
+            if (element._isDirty) {
+              var changedElement = {
+                id: element.attributes.id,
+                styles: addChangedStyles(element),
+                attributes: addChangedAttributes(element),
+                elements: addChangedElements(element, elements),
+              };
+              changedElements.push(changedElement);
+              element._isDirty = false;
+              element._changedAttributes.length = 0;
+              element._changedStyles.length = 0;
+              element._changedElements.length = 0;
+            }
+          }
+          self.postMessage({
+            type: 'updated',
+            payload: {
+              elements: changedElements,
+              id: payload.id,
+              currentTime: event.currentTime,
+            },
+          });
+        });
+      }
+      animations[payload.id] = animation;
+    }
+
+    return {
+      loadAnimation: loadAnimation,
+    };
+  }({}));
+  onmessage = function (evt) {
+    var data = evt.data;
+    var type = data.type;
+    var payload = data.payload;
+    if (type === 'load') {
+      lottieInternal.loadAnimation(payload);
+    } else if (type === 'pause') {
+      if (animations[payload.id]) {
+        animations[payload.id].pause();
+      }
+    } else if (type === 'play') {
+      if (animations[payload.id]) {
+        animations[payload.id].play();
+      }
+    } else if (type === 'stop') {
+      if (animations[payload.id]) {
+        animations[payload.id].stop();
+      }
+    } else if (type === 'setSpeed') {
+      if (animations[payload.id]) {
+        animations[payload.id].setSpeed(payload.value);
+      }
+    } else if (type === 'setDirection') {
+      if (animations[payload.id]) {
+        animations[payload.id].setDirection(payload.value);
+      }
+    } else if (type === 'setDirection') {
+      if (animations[payload.id]) {
+        animations[payload.id].setDirection(payload.value);
+      }
+    } else if (type === 'goToAndPlay') {
+      if (animations[payload.id]) {
+        animations[payload.id].goToAndPlay(payload.value, payload.isFrame);
+      }
+    } else if (type === 'goToAndStop') {
+      if (animations[payload.id]) {
+        animations[payload.id].goToAndStop(payload.value, payload.isFrame);
+      }
+    } else if (type === 'setSubframe') {
+      if (animations[payload.id]) {
+        animations[payload.id].setSubframe(payload.value);
+      }
+    } else if (type === 'addEventListener') {
+      if (animations[payload.id]) {
+        animations[payload.id].addEventListener(payload.eventName, function () {
+          self.postMessage({
+            type: 'event',
+            payload: {
+              id: payload.id,
+              callbackId: payload.callbackId,
+              argument: arguments[0],
+            },
+          });
+        });
+      }
+    } else if (type === 'destroy') {
+      if (animations[payload.id]) {
+        animations[payload.id].destroy();
+        animations[payload.id] = null;
+      }
+    } else if (type === 'resize') {
+      if (animations[payload.id]) {
+        animations[payload.id].resize();
+      }
+    }
+  };
+}
+
+function createWorker(fn) {
+  var blob = new Blob(['(' + fn.toString() + '())'], { type: 'text/javascript' });
+  var url = URL.createObjectURL(blob);
+  return new Worker(url);
+}
+// eslint-disable-next-line no-unused-vars
+var lottie = (function () {
+  'use strict';
+
+  var workerInstance = createWorker(workerContent);
+  var animationIdCounter = 0;
+  var eventsIdCounter = 0;
+  var animations = {};
+  var defaultSettings = {
+    rendererSettings: {},
+  };
+
+  function createTree(data, container, map, afterElement) {
+    var elem;
+    if (data.type === 'div') {
+      elem = document.createElement('div');
+    } else {
+      elem = document.createElementNS(data.namespace, data.type);
+    }
+    for (var attr in data.attributes) {
+      if (Object.prototype.hasOwnProperty.call(data.attributes, attr)) {
+        if (attr === 'href') {
+          elem.setAttributeNS('http://www.w3.org/1999/xlink', attr, data.attributes[attr]);
+        } else {
+          elem.setAttribute(attr, data.attributes[attr]);
+        }
+        if (attr === 'id') {
+          map[data.attributes[attr]] = elem;
+        }
+      }
+    }
+    for (var style in data.style) {
+      if (Object.prototype.hasOwnProperty.call(data.style, style)) {
+        elem.style[style] = data.style[style];
+      }
+    }
+    data.children.forEach(function (element) {
+      createTree(element, elem, map);
+    });
+    if (!afterElement) {
+      container.appendChild(elem);
+    } else {
+      container.insertBefore(elem, afterElement);
+    }
+  }
+
+  var handleAnimationLoaded = (function () {
+    return function (payload) {
+      var animation = animations[payload.id];
+      animation._loaded = true;
+      // if callbacks have been added before the animation has loaded
+      animation.pendingCallbacks.forEach(function (callbackData) {
+        animation.animInstance.addEventListener(callbackData.eventName, callbackData.callback);
+        if (callbackData.eventName === 'DOMLoaded') {
+          callbackData.callback();
+        }
+      });
+      animation.animInstance.totalFrames = payload.totalFrames;
+      animation.animInstance.frameRate = payload.frameRate;
+      var container = animation.container;
+      var elements = animation.elements;
+      createTree(payload.tree, container, elements);
+    };
+  }());
+
+  function addNewElements(newElements, elements) {
+    var element;
+    for (var i = 0; i < newElements.length; i += 1) {
+      element = newElements[i];
+      var parent = elements[element[1]];
+      if (parent) {
+        var sibling;
+        if (element[2]) {
+          sibling = elements[element[2]];
+        }
+        createTree(element[0], parent, elements, sibling);
+        newElements.splice(i, 1);
+        i -= 1;
+      }
+    }
+  }
+
+  function updateElementStyles(element, styles) {
+    var style;
+    for (var i = 0; i < styles.length; i += 1) {
+      style = styles[i];
+      element.style[style[0]] = style[1];
+    }
+  }
+
+  function updateElementAttributes(element, attributes) {
+    var attribute;
+    for (var i = 0; i < attributes.length; i += 1) {
+      attribute = attributes[i];
+      element.setAttribute(attribute[0], attribute[1]);
+    }
+  }
+
+  function handleAnimationUpdate(payload) {
+    var changedElements = payload.elements;
+    var animation = animations[payload.id];
+    if (animation) {
+      var elements = animation.elements;
+      var elementData;
+      for (var i = 0; i < changedElements.length; i += 1) {
+        elementData = changedElements[i];
+        var element = elements[elementData.id];
+        addNewElements(elementData.elements, elements);
+        updateElementStyles(element, elementData.styles);
+        updateElementAttributes(element, elementData.attributes);
+      }
+      animation.animInstance.currentFrame = payload.currentTime;
+    }
+  }
+
+  function handleEvent(payload) {
+    var animation = animations[payload.id];
+    if (animation) {
+      var callbacks = animation.callbacks;
+      if (callbacks[payload.callbackId]) {
+        callbacks[payload.callbackId](payload.argument);
+      }
+    }
+  }
+
+  workerInstance.onmessage = function (event) {
+    if (event.data.type === 'loaded') {
+      handleAnimationLoaded(event.data.payload);
+    } else if (event.data.type === 'updated') {
+      handleAnimationUpdate(event.data.payload);
+    } else if (event.data.type === 'event') {
+      handleEvent(event.data.payload);
+    }
+  };
+
+  function resolveAnimationData(params) {
+    return new Promise(function (resolve, reject) {
+      var paramsCopy = Object.assign({}, defaultSettings, params);
+      if (paramsCopy.animType && !paramsCopy.renderer) {
+        paramsCopy.renderer = paramsCopy.animType;
+      }
+      if (paramsCopy.wrapper) {
+        if (!paramsCopy.container) {
+          paramsCopy.container = paramsCopy.wrapper;
+        }
+        delete paramsCopy.wrapper;
+      }
+      if (paramsCopy.animationData) {
+        resolve(paramsCopy);
+      } else if (paramsCopy.path) {
+        fetch(paramsCopy.path)
+          .then(function (response) {
+            return response.json();
+          })
+          .then(function (animationData) {
+            paramsCopy.animationData = animationData;
+            delete paramsCopy.path;
+            resolve(paramsCopy);
+          });
+      } else {
+        reject();
+      }
+    });
+  }
+
+  function loadAnimation(params) {
+    animationIdCounter += 1;
+    var animationId = 'lottie_animationId_' + animationIdCounter;
+    var animation = {
+      elements: {},
+      callbacks: {},
+      pendingCallbacks: [],
+    };
+    var animInstance = {
+      id: animationId,
+      pause: function () {
+        workerInstance.postMessage({
+          type: 'pause',
+          payload: {
+            id: animationId,
+          },
+        });
+      },
+      play: function () {
+        workerInstance.postMessage({
+          type: 'play',
+          payload: {
+            id: animationId,
+          },
+        });
+      },
+      stop: function () {
+        workerInstance.postMessage({
+          type: 'stop',
+          payload: {
+            id: animationId,
+          },
+        });
+      },
+      setSpeed: function (value) {
+        workerInstance.postMessage({
+          type: 'setSpeed',
+          payload: {
+            id: animationId,
+            value: value,
+          },
+        });
+      },
+      setDirection: function (value) {
+        workerInstance.postMessage({
+          type: 'setDirection',
+          payload: {
+            id: animationId,
+            value: value,
+          },
+        });
+      },
+      goToAndStop: function (value, isFrame) {
+        workerInstance.postMessage({
+          type: 'goToAndStop',
+          payload: {
+            id: animationId,
+            value: value,
+            isFrame: isFrame,
+          },
+        });
+      },
+      goToAndPlay: function (value, isFrame) {
+        workerInstance.postMessage({
+          type: 'goToAndPlay',
+          payload: {
+            id: animationId,
+            value: value,
+            isFrame: isFrame,
+          },
+        });
+      },
+      setSubframe: function (value) {
+        workerInstance.postMessage({
+          type: 'setSubframe',
+          payload: {
+            id: animationId,
+            value: value,
+          },
+        });
+      },
+      addEventListener: function (eventName, callback) {
+        if (!animation._loaded) {
+          animation.pendingCallbacks.push({
+            eventName: eventName,
+            callback: callback,
+          });
+        } else {
+          eventsIdCounter += 1;
+          var callbackId = 'callback_' + eventsIdCounter;
+          animation.callbacks[callbackId] = callback;
+          workerInstance.postMessage({
+            type: 'addEventListener',
+            payload: {
+              id: animationId,
+              callbackId: callbackId,
+              eventName: eventName,
+            },
+          });
+        }
+      },
+      destroy: function () {
+        animations[animationId] = null;
+        if (animation.container) {
+          animation.container.innerHTML = '';
+        }
+        workerInstance.postMessage({
+          type: 'destroy',
+          payload: {
+            id: animationId,
+          },
+        });
+      },
+      resize: function () {
+        workerInstance.postMessage({
+          type: 'resize',
+          payload: {
+            id: animationId,
+          },
+        });
+      },
+    };
+    animation.animInstance = animInstance;
+    resolveAnimationData(params)
+      .then(function (animationParams) {
+        var transferedObjects = [];
+        if (animationParams.container) {
+          animation.container = animationParams.container;
+          delete animationParams.container;
+        }
+        if (animationParams.renderer === 'canvas' && !animationParams.rendererSettings.canvas) {
+          var canvas = document.createElement('canvas');
+          animation.container.appendChild(canvas);
+          canvas.width = animationParams.animationData.w;
+          canvas.height = animationParams.animationData.h;
+          canvas.style.width = '100%';
+          canvas.style.height = '100%';
+          var offscreen = canvas.transferControlToOffscreen();
+          transferedObjects.push(offscreen);
+          animationParams.rendererSettings.canvas = offscreen;
+        }
+        animations[animationId] = animation;
+        workerInstance.postMessage({
+          type: 'load',
+          payload: {
+            params: animationParams,
+            id: animationId,
+          },
+        }, transferedObjects);
+      });
+    return animInstance;
+  }
+
+  var lottiejs = {
+    loadAnimation: loadAnimation,
+  };
+  return lottiejs;
+}());
diff --git a/rollup.config.js b/rollup.config.js
index 32794ed..b829979 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1,7 +1,26 @@
 import { nodeResolve } from '@rollup/plugin-node-resolve';
 import { terser } from 'rollup-plugin-terser';
 import babel from '@rollup/plugin-babel';
-import pkg from './package.json';
+
+// const copyFile = (options = {}) => {
+//   const { targets = [], hook = 'buildEnd' } = options
+//   return {
+//     name: 'copy-file',
+//     // [hook]: async(test) => {
+//     //   console.log('targets', targets.length)
+//     //   console.log('options', options)
+//     //   console.log('test', test)
+//     // },
+//     generateBundle: (options, bundle) => {
+//       // console.log('PASO,. generateBundle', options, bundle)
+//       console.log('PASO,. generateBundle', options, bundle.code)
+//       // bundle.code = '()=>{}';
+//     },
+//     writeBundle: () => {
+//       console.log('PASO,. writeBundle')
+//     }
+//   }
+// } 
 
 const destinationBuildFolder = 'build/player/';
 
@@ -169,7 +188,6 @@
   acc = acc.concat(builds);
   return acc;
 }, []);
-console.log('exports', exports.length);
 export default exports;
 /* export default [
   {
diff --git a/tasks/build_worker.js b/tasks/build_worker.js
new file mode 100644
index 0000000..5d146e7
--- /dev/null
+++ b/tasks/build_worker.js
@@ -0,0 +1,163 @@
+const fs = require('fs');
+const UglifyJS = require("uglify-js");
+
+const buildFolder = 'build/player/';
+const rootFolder = 'player/';
+const bm_version = '5.8.1';
+const defaultBuilds = [ 'canvas_worker', 'lottie_worker']
+
+const scripts = [
+	
+]
+
+function wrapScriptWithModule(code, build) {
+	return new Promise((resolve, reject)=>{
+		try {
+			// Wrapping with module
+			let moduleFileName = 'worker_wrapper';
+			let wrappedCode = fs.readFileSync(`${rootFolder}js/${moduleFileName}.js`, "utf8");
+			wrappedCode = wrappedCode.replace('/* <%= contents %> */',code);
+			wrappedCode = wrappedCode.replace('[[BM_VERSION]]',bm_version);
+			resolve(wrappedCode);
+		} catch(err) {
+			reject(err);
+		}
+	});
+}
+
+function uglifyCode(code) {
+	return new Promise((resolve, reject)=>{
+		try {
+			const result = UglifyJS.minify(code, {
+				output: 
+					{
+						ascii_only:true
+					},
+					toplevel:true,
+					mangle: {
+						reserved: ['lottie']
+					}
+				});
+			if (result.error) {
+				reject(result.error)
+			} else {
+				resolve(result.code)
+			}
+		} catch(err) {
+			reject(err)
+		}
+	})
+}
+
+async function modularizeCode(code, build) {
+	const globalScope = (build =='canvas_worker' || build =='lottie_worker') ? 'self' : 'window'
+	return `(typeof navigator !== "undefined") && (function(root, factory) {
+    if (typeof define === "function" && define.amd) {
+        define(function() {
+            return factory(root);
+        });
+    } else if (typeof module === "object" && module.exports) {
+        module.exports = factory(root);
+    } else {
+        root.lottie = factory(root);
+        root.bodymovin = root.lottie;
+    }
+}((${globalScope} || {}), function(window) {
+	${code}
+return lottie;
+}));`
+}
+
+async function getCode() {
+	try {
+		let scriptsString = '';
+		scriptsString += fs.readFileSync(`${buildFolder}lottie.js`, {encoding: 'utf8'});
+		scriptsString += '\r\n';
+		return scriptsString;
+	} catch(err) {
+		throw err;
+	}
+}
+
+async function buildVersion(version) {
+	try {
+		const code = await getCode(version.build)
+		const wrappedCode = await wrapScriptWithModule(code, version.build)
+		const processedCode = await version.process(wrappedCode)
+		const modularizedCode = await modularizeCode(processedCode, version.build)
+		const saved = await save(modularizedCode, version.fileName)
+		return saved
+	} catch (error) {
+		console.log(error);
+		return null;
+	}
+}
+
+function save(code, fileName) {
+	return new Promise((resolve, reject)=> {
+		fs.writeFile(`${buildFolder}${fileName}`, code, (err) => {
+			if (err) {
+				reject(err)
+			} else {
+				resolve(true)
+			}
+		});
+	})
+}
+
+function noop(code) {
+	return Promise.resolve(code)
+}
+
+function buildVersions() {
+	return new Promise((resolve, reject) => {
+		let versions = [
+			{
+				fileName: 'lottie_canvas_worker.js',
+				build: 'canvas_worker',
+				process: noop
+			},
+			{
+				fileName: 'lottie_canvas_worker.min.js',
+				build: 'canvas_worker',
+				process: uglifyCode
+			},
+			{
+				fileName: 'lottie_worker.js',
+				build: 'lottie_worker',
+				process: noop
+			},
+			{
+				fileName: 'lottie_worker.min.js',
+				build: 'lottie_worker',
+				process: uglifyCode
+			}
+		];
+
+		const buildProcesses = versions.map((version)=>{
+			return buildVersion(version)
+		})
+		Promise.all(buildProcesses)
+		.then(() => {
+			resolve('Build Process Ended')
+		})
+		.catch((err)=>{
+			reject(err)
+		})
+	})
+}
+
+function handleError(err) {
+	console.log(err);
+}
+
+async function build() {
+	try {
+		const result = await buildVersions();
+
+	} catch(err) {
+		handleError(err);
+	}
+}
+
+build()