function p5(sketch, node, sync) {
              var _this = this;
              _classCallCheck(this, p5);
              //////////////////////////////////////////////
              // PUBLIC p5 PROPERTIES AND METHODS
              //////////////////////////////////////////////

              /**
               * Called directly before <a href="#/p5/setup">setup()</a>, the <a href="#/p5/preload">preload()</a> function is used to handle
               * asynchronous loading of external files in a blocking way. If a preload
               * function is defined, <a href="#/p5/setup">setup()</a> will wait until any load calls within have
               * finished. Nothing besides load calls (<a href="#/p5/loadImage">loadImage</a>, <a href="#/p5/loadJSON">loadJSON</a>, <a href="#/p5/loadFont">loadFont</a>,
               * <a href="#/p5/loadStrings">loadStrings</a>, etc.) should be inside the preload function. If asynchronous
               * loading is preferred, the load methods can instead be called in <a href="#/p5/setup">setup()</a>
               * or anywhere else with the use of a callback parameter.
               *
               * By default the text "loading..." will be displayed. To make your own
               * loading page, include an HTML element with id "p5_loading" in your
               * page. More information <a href="http://bit.ly/2kQ6Nio">here</a>.
               *
               * @method preload
               * @example
               * <div><code>
               * let img;
               * let c;
               * function preload() {
               *   // preload() runs once
               *   img = loadImage('assets/laDefense.jpg');
               * }
               *
               * function setup() {
               *   // setup() waits until preload() is done
               *   img.loadPixels();
               *   // get color of middle pixel
               *   c = img.get(img.width / 2, img.height / 2);
               * }
               *
               * function draw() {
               *   background(c);
               *   image(img, 25, 25, 50, 50);
               * }
               * </code></div>
               *
               * @alt
               * nothing displayed
               *
               */

              /**
               * The <a href="#/p5/setup">setup()</a> function is called once when the program starts. It's used to
               * define initial environment properties such as screen size and background
               * color and to load media such as images and fonts as the program starts.
               * There can only be one <a href="#/p5/setup">setup()</a> function for each program and it shouldn't
               * be called again after its initial execution.
               *
               * Note: Variables declared within <a href="#/p5/setup">setup()</a> are not accessible within other
               * functions, including <a href="#/p5/draw">draw()</a>.
               *
               * @method setup
               * @example
               * <div><code>
               * let a = 0;
               *
               * function setup() {
               *   background(0);
               *   noStroke();
               *   fill(102);
               * }
               *
               * function draw() {
               *   rect(a++ % width, 10, 2, 80);
               * }
               * </code></div>
               *
               * @alt
               * nothing displayed
               *
               */

              /**
               * Called directly after <a href="#/p5/setup">setup()</a>, the <a href="#/p5/draw">draw()</a> function continuously executes
               * the lines of code contained inside its block until the program is stopped
               * or <a href="#/p5/noLoop">noLoop()</a> is called. Note if <a href="#/p5/noLoop">noLoop()</a> is called in <a href="#/p5/setup">setup()</a>, <a href="#/p5/draw">draw()</a> will
               * still be executed once before stopping. <a href="#/p5/draw">draw()</a> is called automatically and
               * should never be called explicitly.
               *
               * It should always be controlled with <a href="#/p5/noLoop">noLoop()</a>, <a href="#/p5/redraw">redraw()</a> and <a href="#/p5/loop">loop()</a>. After
               * <a href="#/p5/noLoop">noLoop()</a> stops the code in <a href="#/p5/draw">draw()</a> from executing, <a href="#/p5/redraw">redraw()</a> causes the
               * code inside <a href="#/p5/draw">draw()</a> to execute once, and <a href="#/p5/loop">loop()</a> will cause the code
               * inside <a href="#/p5/draw">draw()</a> to resume executing continuously.
               *
               * The number of times <a href="#/p5/draw">draw()</a> executes in each second may be controlled with
               * the <a href="#/p5/frameRate">frameRate()</a> function.
               *
               * There can only be one <a href="#/p5/draw">draw()</a> function for each sketch, and <a href="#/p5/draw">draw()</a> must
               * exist if you want the code to run continuously, or to process events such
               * as <a href="#/p5/mousePressed">mousePressed()</a>. Sometimes, you might have an empty call to <a href="#/p5/draw">draw()</a> in
               * your program, as shown in the above example.
               *
               * It is important to note that the drawing coordinate system will be reset
               * at the beginning of each <a href="#/p5/draw">draw()</a> call. If any transformations are performed
               * within <a href="#/p5/draw">draw()</a> (ex: scale, rotate, translate), their effects will be
               * undone at the beginning of <a href="#/p5/draw">draw()</a>, so transformations will not accumulate
               * over time. On the other hand, styling applied (ex: fill, stroke, etc) will
               * remain in effect.
               *
               * @method draw
               * @example
               * <div><code>
               * let yPos = 0;
               * function setup() {
               *   // setup() runs once
               *   frameRate(30);
               * }
               * function draw() {
               *   // draw() loops forever, until stopped
               *   background(204);
               *   yPos = yPos - 1;
               *   if (yPos < 0) {
               *     yPos = height;
               *   }
               *   line(0, yPos, width, yPos);
               * }
               * </code></div>
               *
               * @alt
               * nothing displayed
               *
               */

              //////////////////////////////////////////////
              // PRIVATE p5 PROPERTIES AND METHODS
              //////////////////////////////////////////////

              this._setupDone = false;
              // for handling hidpi
              this._pixelDensity = Math.ceil(window.devicePixelRatio) || 1;
              this._userNode = node;
              this._curElement = null;
              this._elements = [];
              this._glAttributes = null;
              this._requestAnimId = 0;
              this._preloadCount = 0;
              this._isGlobal = false;
              this._loop = true;
              this._initializeInstanceVariables();
              this._defaultCanvasSize = {
                width: 100,
                height: 100
              };

              this._events = {
                // keep track of user-events for unregistering later
                mousemove: null,
                mousedown: null,
                mouseup: null,
                dragend: null,
                dragover: null,
                click: null,
                dblclick: null,
                mouseover: null,
                mouseout: null,
                keydown: null,
                keyup: null,
                keypress: null,
                touchstart: null,
                touchmove: null,
                touchend: null,
                resize: null,
                blur: null
              };

              this._millisStart = -1;

              // States used in the custom random generators
              this._lcg_random_state = null;
              this._gaussian_previous = false;

              this._events.wheel = null;
              this._loadingScreenId = 'p5_loading';

              // Allows methods to be registered on an instance that
              // are instance-specific.
              this._registeredMethods = {};
              var methods = Object.getOwnPropertyNames(p5.prototype._registeredMethods);
              var _iteratorNormalCompletion = true;
              var _didIteratorError = false;
              var _iteratorError = undefined;
              try {
                for (
                  var _iterator = methods[Symbol.iterator](), _step;
                  !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
                  _iteratorNormalCompletion = true
                ) {
                  var prop = _step.value;
                  this._registeredMethods[prop] = p5.prototype._registeredMethods[
                    prop
                  ].slice();
                }
              } catch (err) {
                _didIteratorError = true;
                _iteratorError = err;
              } finally {
                try {
                  if (!_iteratorNormalCompletion && _iterator.return != null) {
                    _iterator.return();
                  }
                } finally {
                  if (_didIteratorError) {
                    throw _iteratorError;
                  }
                }
              }

              if (window.DeviceOrientationEvent) {
                this._events.deviceorientation = null;
              }
              if (window.DeviceMotionEvent && !window._isNodeWebkit) {
                this._events.devicemotion = null;
              }

              this._start = function() {
                // Find node if id given
                if (_this._userNode) {
                  if (typeof _this._userNode === 'string') {
                    _this._userNode = document.getElementById(_this._userNode);
                  }
                }

                var context = _this._isGlobal ? window : _this;
                if (context.preload) {
                  // Setup loading screen
                  // Set loading screen into dom if not present
                  // Otherwise displays and removes user provided loading screen
                  var loadingScreen = document.getElementById(_this._loadingScreenId);
                  if (!loadingScreen) {
                    loadingScreen = document.createElement('div');
                    loadingScreen.innerHTML = 'Loading...';
                    loadingScreen.style.position = 'absolute';
                    loadingScreen.id = _this._loadingScreenId;
                    var _node = _this._userNode || document.body;
                    _node.appendChild(loadingScreen);
                  }
                  var _methods = _this._preloadMethods;
                  for (var method in _methods) {
                    // default to p5 if no object defined
                    _methods[method] = _methods[method] || p5;
                    var obj = _methods[method];
                    //it's p5, check if it's global or instance
                    if (obj === p5.prototype || obj === p5) {
                      if (_this._isGlobal) {
                        window[method] = _this._wrapPreload(_this, method);
                      }
                      obj = _this;
                    }
                    _this._registeredPreloadMethods[method] = obj[method];
                    obj[method] = _this._wrapPreload(obj, method);
                  }

                  context.preload();
                  _this._runIfPreloadsAreDone();
                } else {
                  _this._setup();
                  _this._draw();
                }
              };

              this._runIfPreloadsAreDone = function() {
                var context = this._isGlobal ? window : this;
                if (context._preloadCount === 0) {
                  var loadingScreen = document.getElementById(context._loadingScreenId);
                  if (loadingScreen) {
                    loadingScreen.parentNode.removeChild(loadingScreen);
                  }
                  if (!this._setupDone) {
                    this._lastFrameTime = window.performance.now();
                    context._setup();
                    context._draw();
                  }
                }
              };

              this._decrementPreload = function() {
                var context = this._isGlobal ? window : this;
                if (typeof context.preload === 'function') {
                  context._setProperty('_preloadCount', context._preloadCount - 1);
                  context._runIfPreloadsAreDone();
                }
              };

              this._wrapPreload = function(obj, fnName) {
                var _this2 = this;
                return function() {
                  //increment counter
                  _this2._incrementPreload();
                  //call original function
                  for (
                    var _len = arguments.length, args = new Array(_len), _key = 0;
                    _key < _len;
                    _key++
                  ) {
                    args[_key] = arguments[_key];
                  }
                  return _this2._registeredPreloadMethods[fnName].apply(obj, args);
                };
              };

              this._incrementPreload = function() {
                var context = this._isGlobal ? window : this;
                context._setProperty('_preloadCount', context._preloadCount + 1);
              };

              this._setup = function() {
                // Always create a default canvas.
                // Later on if the user calls createCanvas, this default one
                // will be replaced
                _this.createCanvas(
                  _this._defaultCanvasSize.width,
                  _this._defaultCanvasSize.height,
                  'p2d'
                );

                // return preload functions to their normal vals if switched by preload
                var context = _this._isGlobal ? window : _this;
                if (typeof context.preload === 'function') {
                  for (var f in _this._preloadMethods) {
                    context[f] = _this._preloadMethods[f][f];
                    if (context[f] && _this) {
                      context[f] = context[f].bind(_this);
                    }
                  }
                }

                // Record the time when sketch starts
                _this._millisStart = window.performance.now();

                // Short-circuit on this, in case someone used the library in "global"
                // mode earlier
                if (typeof context.setup === 'function') {
                  context.setup();
                }

                // unhide any hidden canvases that were created
                var canvases = document.getElementsByTagName('canvas');
                var _iteratorNormalCompletion2 = true;
                var _didIteratorError2 = false;
                var _iteratorError2 = undefined;
                try {
                  for (
                    var _iterator2 = canvases[Symbol.iterator](), _step2;
                    !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done);
                    _iteratorNormalCompletion2 = true
                  ) {
                    var k = _step2.value;
                    if (k.dataset.hidden === 'true') {
                      k.style.visibility = '';
                      delete k.dataset.hidden;
                    }
                  }
                } catch (err) {
                  _didIteratorError2 = true;
                  _iteratorError2 = err;
                } finally {
                  try {
                    if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
                      _iterator2.return();
                    }
                  } finally {
                    if (_didIteratorError2) {
                      throw _iteratorError2;
                    }
                  }
                }

                _this._lastFrameTime = window.performance.now();
                _this._setupDone = true;
                if (_this._accessibleOutputs.grid || _this._accessibleOutputs.text) {
                  _this._updateAccsOutput();
                }
              };

              this._draw = function() {
                var now = window.performance.now();
                var time_since_last = now - _this._lastFrameTime;
                var target_time_between_frames = 1000 / _this._targetFrameRate;

                // only draw if we really need to; don't overextend the browser.
                // draw if we're within 5ms of when our next frame should paint
                // (this will prevent us from giving up opportunities to draw
                // again when it's really about time for us to do so). fixes an
                // issue where the frameRate is too low if our refresh loop isn't
                // in sync with the browser. note that we have to draw once even
                // if looping is off, so we bypass the time delay if that
                // is the case.
                var epsilon = 5;
                if (
                  !_this._loop ||
                  time_since_last >= target_time_between_frames - epsilon
                ) {
                  //mandatory update values(matrixes and stack)
                  _this.redraw();
                  _this._frameRate = 1000.0 / (now - _this._lastFrameTime);
                  _this.deltaTime = now - _this._lastFrameTime;
                  _this._setProperty('deltaTime', _this.deltaTime);
                  _this._lastFrameTime = now;

                  // If the user is actually using mouse module, then update
                  // coordinates, otherwise skip. We can test this by simply
                  // checking if any of the mouse functions are available or not.
                  // NOTE : This reflects only in complete build or modular build.
                  if (typeof _this._updateMouseCoords !== 'undefined') {
                    _this._updateMouseCoords();

                    //reset delta values so they reset even if there is no mouse event to set them
                    // for example if the mouse is outside the screen
                    _this._setProperty('movedX', 0);
                    _this._setProperty('movedY', 0);
                  }
                }

                // get notified the next time the browser gives us
                // an opportunity to draw.
                if (_this._loop) {
                  _this._requestAnimId = window.requestAnimationFrame(_this._draw);
                }
              };

              this._setProperty = function(prop, value) {
                _this[prop] = value;
                if (_this._isGlobal) {
                  window[prop] = value;
                }
              };

              /**
               * Removes the entire p5 sketch. This will remove the canvas and any
               * elements created by p5.js. It will also stop the draw loop and unbind
               * any properties or methods from the window global scope. It will
               * leave a variable p5 in case you wanted to create a new p5 sketch.
               * If you like, you can set p5 = null to erase it. While all functions and
               * variables and objects created by the p5 library will be removed, any
               * other global variables created by your code will remain.
               *
               * @method remove
               * @example
               * <div class='norender'><code>
               * function draw() {
               *   ellipse(50, 50, 10, 10);
               * }
               *
               * function mousePressed() {
               *   remove(); // remove whole sketch on mouse press
               * }
               * </code></div>
               *
               * @alt
               * nothing displayed
               *
               */
              this.remove = function() {
                var loadingScreen = document.getElementById(_this._loadingScreenId);
                if (loadingScreen) {
                  loadingScreen.parentNode.removeChild(loadingScreen);
                  // Add 1 to preload counter to prevent the sketch ever executing setup()
                  _this._incrementPreload();
                }
                if (_this._curElement) {
                  // stop draw
                  _this._loop = false;
                  if (_this._requestAnimId) {
                    window.cancelAnimationFrame(_this._requestAnimId);
                  }

                  // unregister events sketch-wide
                  for (var ev in _this._events) {
                    window.removeEventListener(ev, _this._events[ev]);
                  }

                  // remove DOM elements created by p5, and listeners
                  var _iteratorNormalCompletion3 = true;
                  var _didIteratorError3 = false;
                  var _iteratorError3 = undefined;
                  try {
                    for (
                      var _iterator3 = _this._elements[Symbol.iterator](), _step3;
                      !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done);
                      _iteratorNormalCompletion3 = true
                    ) {
                      var e = _step3.value;
                      if (e.elt && e.elt.parentNode) {
                        e.elt.parentNode.removeChild(e.elt);
                      }
                      for (var elt_ev in e._events) {
                        e.elt.removeEventListener(elt_ev, e._events[elt_ev]);
                      }
                    }

                    // call any registered remove functions
                  } catch (err) {
                    _didIteratorError3 = true;
                    _iteratorError3 = err;
                  } finally {
                    try {
                      if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
                        _iterator3.return();
                      }
                    } finally {
                      if (_didIteratorError3) {
                        throw _iteratorError3;
                      }
                    }
                  }
                  var self = _this;
                  _this._registeredMethods.remove.forEach(function(f) {
                    if (typeof f !== 'undefined') {
                      f.call(self);
                    }
                  });
                }
                // remove window bound properties and methods
                if (_this._isGlobal) {
                  for (var p in p5.prototype) {
                    try {
                      delete window[p];
                    } catch (x) {
                      window[p] = undefined;
                    }
                  }
                  for (var p2 in _this) {
                    if (_this.hasOwnProperty(p2)) {
                      try {
                        delete window[p2];
                      } catch (x) {
                        window[p2] = undefined;
                      }
                    }
                  }
                  p5.instance = null;
                }
              };

              // call any registered init functions
              this._registeredMethods.init.forEach(function(f) {
                if (typeof f !== 'undefined') {
                  f.call(this);
                }
              }, this);
              // Set up promise preloads
              this._setupPromisePreloads();

              var friendlyBindGlobal = this._createFriendlyGlobalFunctionBinder();

              // If the user has created a global setup or draw function,
              // assume "global" mode and make everything global (i.e. on the window)
              if (!sketch) {
                this._isGlobal = true;
                p5.instance = this;
                // Loop through methods on the prototype and attach them to the window
                for (var p in p5.prototype) {
                  if (typeof p5.prototype[p] === 'function') {
                    var ev = p.substring(2);
                    if (!this._events.hasOwnProperty(ev)) {
                      if (Math.hasOwnProperty(p) && Math[p] === p5.prototype[p]) {
                        // Multiple p5 methods are just native Math functions. These can be
                        // called without any binding.
                        friendlyBindGlobal(p, p5.prototype[p]);
                      } else {
                        friendlyBindGlobal(p, p5.prototype[p].bind(this));
                      }
                    }
                  } else {
                    friendlyBindGlobal(p, p5.prototype[p]);
                  }
                }
                // Attach its properties to the window
                for (var p2 in this) {
                  if (this.hasOwnProperty(p2)) {
                    friendlyBindGlobal(p2, this[p2]);
                  }
                }
              } else {
                // Else, the user has passed in a sketch closure that may set
                // user-provided 'setup', 'draw', etc. properties on this instance of p5
                sketch(this);

                // Run a check to see if the user has misspelled 'setup', 'draw', etc
                // detects capitalization mistakes only ( Setup, SETUP, MouseClicked, etc)
                p5._checkForUserDefinedFunctions(this);
              }

              // Bind events to window (not using container div bc key events don't work)

              for (var e in this._events) {
                var f = this['_on'.concat(e)];
                if (f) {
                  var m = f.bind(this);
                  window.addEventListener(e, m, { passive: false });
                  this._events[e] = m;
                }
              }

              var focusHandler = function focusHandler() {
                _this._setProperty('focused', true);
              };
              var blurHandler = function blurHandler() {
                _this._setProperty('focused', false);
              };
              window.addEventListener('focus', focusHandler);
              window.addEventListener('blur', blurHandler);
              this.registerMethod('remove', function() {
                window.removeEventListener('focus', focusHandler);
                window.removeEventListener('blur', blurHandler);
              });

              if (document.readyState === 'complete') {
                this._start();
              } else {
                window.addEventListener('load', this._start.bind(this), false);
              }
            }
This is the p5 instance constructor.

A p5 instance holds all the properties and methods related to
a p5 sketch.  It expects an incoming sketch closure and it can also
take an optional node parameter for attaching the generated p5 canvas
to a node.  The sketch closure takes the newly created p5 instance as
its sole argument and may optionally set <a href="#/p5/preload">preload()</a>,
<a href="#/p5/setup">setup()</a>, and/or
<a href="#/p5/draw">draw()</a> properties on it for running a sketch.

A p5 sketch can run in "global" or "instance" mode:
"global"   - all properties and methods are attached to the window
"instance" - all properties and methods are bound to this p5 object

@class p5
@constructor
@param  {function}           sketch a closure that can set optional <a href="#/p5/preload">preload()</a>,
                             <a href="#/p5/setup">setup()</a>, and/or <a href="#/p5/draw">draw()</a> properties on the
                             given p5 instance
@param  {HTMLElement}        [node] element to attach canvas to
@return {p5}                 a p5 instance