diff --git a/examples/p5js/grid24x8_clouds.html b/examples/p5js/grid24x8_clouds.html
new file mode 100644
index 0000000000000000000000000000000000000000..f010525730c044e024e89d28960e91de1d9d31d0
--- /dev/null
+++ b/examples/p5js/grid24x8_clouds.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>grid24x8z_clouds</title>
+    <script src="libraries/p5.js" type="text/javascript"></script>
+
+    <script src="libraries/p5.dom.js" type="text/javascript"></script>
+    <script src="libraries/p5.sound.js" type="text/javascript"></script>
+	<script src="libraries/opc.js" type="text/javascript"></script>
+    <script src="p5js_sketches/grid24x8_clouds.js" type="text/javascript"></script>
+
+    <style> body {padding: 0; margin: 0;} canvas {vertical-align: top;} </style>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/examples/p5js/grid8x8_dot.html b/examples/p5js/grid8x8_dot.html
new file mode 100644
index 0000000000000000000000000000000000000000..08971382da2d3f15477d1158e2280e48f3666d1d
--- /dev/null
+++ b/examples/p5js/grid8x8_dot.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>grid8x8_dot</title>
+    <script src="libraries/p5.js" type="text/javascript"></script>
+    <script src="libraries/p5.dom.js" type="text/javascript"></script>
+    <script src="libraries/p5.sound.js" type="text/javascript"></script>
+		<script src="libraries/opc.js" type="text/javascript"></script>
+    <script src="p5js_sketches/grid8x8_dot.js" type="text/javascript"></script>
+
+    <style> body {padding: 0; margin: 0;} canvas {vertical-align: top;} </style>
+  </head>
+  <body>
+  </body>
+</html>
\ No newline at end of file
diff --git a/examples/p5js/grid8x8_noise_simple.html b/examples/p5js/grid8x8_noise_simple.html
new file mode 100644
index 0000000000000000000000000000000000000000..7305990623c684259fa6f98dec56d567e6552ea3
--- /dev/null
+++ b/examples/p5js/grid8x8_noise_simple.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>grid8x8_dot</title>
+    <script src="libraries/p5.js" type="text/javascript"></script>
+    <script src="libraries/p5.dom.js" type="text/javascript"></script>
+    <script src="libraries/p5.sound.js" type="text/javascript"></script>
+	<script src="libraries/opc.js" type="text/javascript"></script>
+    <script src="p5js_sketches/grid8x8_simple_noise.js" type="text/javascript"></script>
+
+    <style> body {padding: 0; margin: 0;} canvas {vertical-align: top;} </style>
+  </head>
+  <body>
+  </body>
+</html>
\ No newline at end of file
diff --git a/examples/p5js/grid8x8_orbits.html b/examples/p5js/grid8x8_orbits.html
new file mode 100644
index 0000000000000000000000000000000000000000..56f89f2ab9f28161f843df6379db35d0f217be35
--- /dev/null
+++ b/examples/p5js/grid8x8_orbits.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>Grid 8x8 Orbits</title>
+    <script src="libraries/p5.js" type="text/javascript"></script>
+
+    <script src="libraries/p5.dom.js" type="text/javascript"></script>
+    <script src="libraries/p5.sound.js" type="text/javascript"></script>
+	<script src="libraries/opc.js" type="text/javascript"></script>
+    <script src="p5js_sketches/grid8x8_orbits.js" type="text/javascript"></script>
+
+    <style> body {padding: 0; margin: 0;} canvas {vertical-align: top;} </style>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/examples/p5js/images/dot.png b/examples/p5js/images/dot.png
new file mode 100644
index 0000000000000000000000000000000000000000..eee5297756163e940bc7036a51557a4d07df5ffa
Binary files /dev/null and b/examples/p5js/images/dot.png differ
diff --git a/examples/p5js/images/flames.jpeg b/examples/p5js/images/flames.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..f91518b6a0f5054eb33f60f97a80bc5935a90de0
Binary files /dev/null and b/examples/p5js/images/flames.jpeg differ
diff --git a/examples/p5js/images/greenDot.png b/examples/p5js/images/greenDot.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb79d40215ba868d865984b91b0f60f95cd5e00b
Binary files /dev/null and b/examples/p5js/images/greenDot.png differ
diff --git a/examples/p5js/images/purpleDot.png b/examples/p5js/images/purpleDot.png
new file mode 100644
index 0000000000000000000000000000000000000000..0e1b93d3f298fc425a9128e9999c47844dc525ff
Binary files /dev/null and b/examples/p5js/images/purpleDot.png differ
diff --git a/examples/p5js/libraries/opc.js b/examples/p5js/libraries/opc.js
new file mode 100644
index 0000000000000000000000000000000000000000..f1daaaefe848501a25c42a0110aa52f5bb011c7f
--- /dev/null
+++ b/examples/p5js/libraries/opc.js
@@ -0,0 +1,160 @@
+/*
+ * Simple Open Pixel Control client for P5js,
+ * designed to sample each LED's color from some point on the canvas.
+ *
+ * Micah Elizabeth Scott, 2013
+ * Ported to P5js by Matthew I. Kessler
+ * This file is released into the public domain.
+ */
+
+/*
+ *!!THIS SCRIPT MUST BE LOADED IN HTML BEFORE THE DRAW SCRIPT!!
+ *
+ *Example for HTML head:
+ *<script src="libraries/opc.js" type="text/javascript"></script>
+ *<script src="strip64_flames.js" type="text/javascript"></script>
+ */
+
+// Arrays for pixels[]'s locations to send rgb values to fcServer.
+var pixelLocationsRed = [];
+var pixelLocationsGre = [];
+var pixelLocationsBlu = [];
+
+// Arrays for to map pixels on screen.
+var ledXpoints = [];
+var ledYpoints = [];
+
+// Enable locations on screen.
+var enableShowLocations;
+
+//New WebSocket.
+var socket;
+function socketSetup(WebSocketAddress) {
+	socket = new WebSocket(WebSocketAddress);
+	enableShowLocations = true;
+}
+
+// Set the location of a single LED.
+function led(index, x, y) {
+	loadPixels();
+	if (pixelLocationsRed === null) {
+		pixelLocationsRed.length = index + 1;
+		pixelLocationsGre.length = index + 1;
+		pixelLocationsBlu.length = index + 1;
+		ledXpoints.length = index + 1;
+		ledYpoints.length = index + 1;
+	} else if (index >= pixelLocationsRed.length) {
+		pixelLocationsRed.length = index + 1;
+		pixelLocationsGre.length = index + 1;
+		pixelLocationsBlu.length = index + 1;
+		ledXpoints.length = index + 1;
+		ledYpoints.length = index + 1;
+	}
+	//Store pixel[] map to color arrays.
+	var pixelD = pixelDensity();
+	var idx = pixelD*pixelD*4*y*width+x*pixelD*4;
+	pixelLocationsRed[index] = (idx);
+	pixelLocationsGre[index] = (idx + 1);
+	pixelLocationsBlu[index] = (idx + 2);
+	//Store x,y to draw points for pixel locations 
+	ledXpoints[index] = x;
+	ledYpoints[index] = y;
+}
+
+// Set the location of several LEDs arranged in a strip.
+// Angle is in radians, measured clockwise from +X.
+// (x,y) is the center of the strip.
+function ledStrip(index, count, x, y, spacing, angle, reversed) {
+	var s = sin(angle);
+	var c = cos(angle);
+	for (var i = 0; i < count; i++) {
+		led(
+			reversed ? (index + count - 1 - i) * 1 : (index + i) * 1,
+			//floor() These must be integers.  round() causes lag
+			floor((x + (i - (count - 1) / 2.0) * spacing * c + 0.5) * 1),
+			floor((y + (i - (count - 1) / 2.0) * spacing * s + 0.5) * 1));
+	}
+}
+
+// Set the locations of a ring of LEDs. The center of the ring is at (x, y),
+// with "radius" pixels between the center and each LED. The first LED is at
+// the indicated angle, in radians, measured clockwise from +X.
+function ledRing(index, count, x, y, radius, angle) {
+	for (var i = 0; i < count; i++) {
+		var a = angle + i * 2 * PI / count;
+		led(
+			index + i,
+			floor((x - radius * cos(a) + 0.5)),
+			floor((y - radius * sin(a) + 0.5))
+		);
+	}
+}
+
+// Set the location of several LEDs arranged in a grid. The first strip is
+// at 'angle', measured in radians clockwise from +X.
+// (x,y) is the center of the grid.
+function ledGrid(index, stripLength, numStrips, x, y, ledSpacing, stripSpacing, angle, zigzag) {
+	var s = sin(angle + HALF_PI);
+	var c = cos(angle + HALF_PI);
+	for (var i = 0; i < numStrips; i++) {
+		ledStrip(
+			index + stripLength * i,
+			stripLength,
+			x + (i - (numStrips - 1) / 2.0) * stripSpacing * c,
+			y + (i - (numStrips - 1) / 2.0) * stripSpacing * s,
+			ledSpacing,
+			angle,
+			zigzag && (i % 2) == 1);
+	}
+}
+
+// Set the location of 64 LEDs arranged in a uniform 8x8 grid.
+// (x,y) is the center of the grid.
+function ledGrid8x8(index, x, y, spacing, angle, zigzag) {
+	ledGrid(index, 8, 8, x, y, spacing, spacing, angle, zigzag);
+}
+
+//Called in function draw(){...} on last line.
+function drawFrame() {
+	if (pixelLocationsRed === null) {
+		// No pixels defined yet
+		return;
+	}
+	var leds = pixelLocationsRed.length;
+	var packet = new Uint8ClampedArray(4 + leds * 3);
+
+	if (socket.readyState != 1 /* OPEN */ ) {
+		// The server connection isn't open. Nothing to do.
+		return;
+	}
+
+	if (socket.bufferedAmount > packet.length) {
+		// The network is lagging, and we still haven't sent the previous frame.
+		// Don't flood the network, it will just make us laggy.
+		// If fcserver is running on the same computer, it should always be able
+		// to keep up with the frames we send, so we shouldn't reach this point.
+		return;
+	}
+
+	// Dest position in our packet. Start right after the header.
+	var dest = 4;
+	loadPixels();
+
+	// Sample and send the center pixel of each LED
+	for (var led = 0; led < leds; led++) {
+		var i = led;
+		packet[dest++] = pixels[pixelLocationsRed[i]];
+		packet[dest++] = pixels[pixelLocationsGre[i]];
+		packet[dest++] = pixels[pixelLocationsBlu[i]];
+	}
+	socket.send(packet.buffer);
+
+	//draw pixel locations on screen if enabled
+	if (showPixelLocations === true) {
+		for (i = 0; i < leds; i++) {
+			stroke(127);
+			//offset x+1 and y+1 so we don't send the dots to the fc Server
+			point(ledXpoints[i]+1, ledYpoints[i]+1);
+		}
+	}
+}
\ No newline at end of file
diff --git a/examples/p5js/libraries/p5.dom.js b/examples/p5js/libraries/p5.dom.js
new file mode 100644
index 0000000000000000000000000000000000000000..d4b84602c0ba7943a6d70a1d9500b935d6e8fe8e
--- /dev/null
+++ b/examples/p5js/libraries/p5.dom.js
@@ -0,0 +1,2034 @@
+/*! p5.dom.js v0.2.9 March 3, 2016 */
+/**
+ * <p>The web is much more than just canvas and p5.dom makes it easy to interact
+ * with other HTML5 objects, including text, hyperlink, image, input, video,
+ * audio, and webcam.</p>
+ * <p>There is a set of creation methods, DOM manipulation methods, and
+ * an extended p5.Element that supports a range of HTML elements. See the
+ * <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">
+ * beyond the canvas tutorial</a> for a full overview of how this addon works.
+ *
+ * <p>Methods and properties shown in black are part of the p5.js core, items in
+ * blue are part of the p5.dom library. You will need to include an extra file
+ * in order to access the blue functions. See the
+ * <a href="http://p5js.org/libraries/#using-a-library">using a library</a>
+ * section for information on how to include this library. p5.dom comes with
+ * <a href="http://p5js.org/download">p5 complete</a> or you can download the single file
+ * <a href="https://raw.githubusercontent.com/lmccart/p5.js/master/lib/addons/p5.dom.js">
+ * here</a>.</p>
+ * <p>See <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">tutorial: beyond the canvas</a>
+ * for more info on how to use this libary.</a>
+ *
+ * @module p5.dom
+ * @submodule p5.dom
+ * @for p5.dom
+ * @main
+ */
+
+(function (root, factory) {
+  if (typeof define === 'function' && define.amd)
+    define('p5.dom', ['p5'], function (p5) { (factory(p5));});
+  else if (typeof exports === 'object')
+    factory(require('../p5'));
+  else
+    factory(root['p5']);
+}(this, function (p5) {
+// =============================================================================
+//                         p5 additions
+// =============================================================================
+
+  /**
+   * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.'
+   * prefixes to specify an ID or class respectively, and none for a tag) and returns it as
+   * a p5.Element. If a class or tag name is given with more than 1 element,
+   * only the first element will be returned.
+   * The DOM node itself can be accessed with .elt.
+   * Returns null if none found. You can also specify a container to search within.
+   *
+   * @method select
+   * @param  {String} name id, class, or tag name of element to search for
+   * @param  {String} [container] id, p5.Element, or HTML element to search within
+   * @return {Object/p5.Element|Null} p5.Element containing node found
+   * @example
+   * <div ><code class='norender'>
+   * function setup() {
+   *   createCanvas(100,100);
+   *   //translates canvas 50px down
+   *   select('canvas').position(100, 100);
+   * }
+   * </code></div>
+   * <div ><code class='norender'>
+   * // these are all valid calls to select()
+   * var a = select('#moo');
+   * var b = select('#blah', '#myContainer');
+   * var c = select('#foo', b);
+   * var d = document.getElementById('beep');
+   * var e = select('p', d);
+   * </code></div>
+   *
+   */
+  p5.prototype.select = function (e, p) {
+    var res = null;
+    var container = getContainer(p);
+    if (e[0] === '.'){
+      e = e.slice(1);
+      res = container.getElementsByClassName(e);
+      if (res.length) {
+        res = res[0];
+      } else {
+        res = null;
+      }
+    }else if (e[0] === '#'){
+      e = e.slice(1);
+      res = container.getElementById(e);
+    }else {
+      res = container.getElementsByTagName(e);
+      if (res.length) {
+        res = res[0];
+      } else {
+        res = null;
+      }
+    }
+    if (res) {
+      return wrapElement(res);
+    } else {
+      return null;
+    }
+  };
+
+  /**
+   * Searches the page for elements with the given class or tag name (using the '.' prefix
+   * to specify a class and no prefix for a tag) and returns them as p5.Elements
+   * in an array.
+   * The DOM node itself can be accessed with .elt.
+   * Returns an empty array if none found.
+   * You can also specify a container to search within.
+   *
+   * @method selectAll
+   * @param  {String} name class or tag name of elements to search for
+   * @param  {String} [container] id, p5.Element, or HTML element to search within
+   * @return {Array} Array of p5.Elements containing nodes found
+   * @example
+   * <div ><code class='norender'>
+   * function setup() {
+   *   createButton('btn');
+   *   createButton('2nd btn');
+   *   createButton('3rd btn');
+   *   var buttons = selectAll('button');
+   *
+   *   for (var i = 0; i < buttons.length; i++){
+   *     buttons[i].size(100,100);
+   *   }
+   * }
+   * </code></div>
+   * <div ><code class='norender'>
+   * // these are all valid calls to selectAll()
+   * var a = selectAll('.moo');
+   * var b = selectAll('div');
+   * var c = selectAll('button', '#myContainer');
+   * var d = select('#container');
+   * var e = selectAll('p', d);
+   * var f = document.getElementById('beep');
+   * var g = select('.blah', f);
+   * </code></div>
+   *
+   */
+  p5.prototype.selectAll = function (e, p) {
+    var arr = [];
+    var res;
+    var container = getContainer(p);
+    if (e[0] === '.'){
+      e = e.slice(1);
+      res = container.getElementsByClassName(e);
+    } else {
+      res = container.getElementsByTagName(e);
+    }
+    if (res) {
+      for (var j = 0; j < res.length; j++) {
+        var obj = wrapElement(res[j]);
+        arr.push(obj);
+      }
+    }
+    return arr;
+  };
+
+  /**
+   * Helper function for select and selectAll
+   */
+  function getContainer(p) {
+    var container = document;
+    if (typeof p === 'string' && p[0] === '#'){
+      p = p.slice(1);
+      container = document.getElementById(p) || document;
+    } else if (p instanceof p5.Element){
+      container = p.elt;
+    } else if (p instanceof HTMLElement){
+      container = p;
+    }
+    return container;
+  }
+
+  /**
+   * Helper function for getElement and getElements.
+   */
+  function wrapElement(elt) {
+    if(elt.tagName === "INPUT" && elt.type === "checkbox") {
+      var converted = new p5.Element(elt);
+      converted.checked = function(){
+      if (arguments.length === 0){
+        return this.elt.checked;
+      } else if(arguments[0]) {
+        this.elt.checked = true;
+      } else {
+        this.elt.checked = false;
+      }
+      return this;
+      };
+      return converted;
+    } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") {
+      return new p5.MediaElement(elt);
+    } else {
+      return new p5.Element(elt);
+    }
+  }
+
+  /**
+   * Removes all elements created by p5, except any canvas / graphics
+   * elements created by createCanvas or createGraphics.
+   * Event handlers are removed, and element is removed from the DOM.
+   * @method removeElements
+   * @example
+   * <div class='norender'><code>
+   * function setup() {
+   *   createCanvas(100, 100);
+   *   createDiv('this is some text');
+   *   createP('this is a paragraph');
+   * }
+   * function mousePressed() {
+   *   removeElements(); // this will remove the div and p, not canvas
+   * }
+   * </code></div>
+   *
+   */
+  p5.prototype.removeElements = function (e) {
+    for (var i=0; i<this._elements.length; i++) {
+      if (!(this._elements[i].elt instanceof HTMLCanvasElement)) {
+        this._elements[i].remove();
+      }
+    }
+  };
+
+  /**
+   * Helpers for create methods.
+   */
+  function addElement(elt, pInst, media) {
+    var node = pInst._userNode ? pInst._userNode : document.body;
+    node.appendChild(elt);
+    var c = media ? new p5.MediaElement(elt) : new p5.Element(elt);
+    pInst._elements.push(c);
+    return c;
+  }
+
+  /**
+   * Creates a &lt;div&gt;&lt;/div&gt; element in the DOM with given inner HTML.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createDiv
+   * @param  {String} html inner HTML for element created
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var myDiv;
+   * function setup() {
+   *   myDiv = createDiv('this is some text');
+   * }
+   * </code></div>
+   */
+
+  /**
+   * Creates a &lt;p&gt;&lt;/p&gt; element in the DOM with given inner HTML. Used
+   * for paragraph length text.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createP
+   * @param  {String} html inner HTML for element created
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var myP;
+   * function setup() {
+   *   myP = createP('this is some text');
+   * }
+   * </code></div>
+   */
+
+  /**
+   * Creates a &lt;span&gt;&lt;/span&gt; element in the DOM with given inner HTML.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createSpan
+   * @param  {String} html inner HTML for element created
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var mySpan;
+   * function setup() {
+   *   mySpan = createSpan('this is some text');
+   * }
+   * </code></div>
+   */
+  var tags = ['div', 'p', 'span'];
+  tags.forEach(function(tag) {
+    var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
+    p5.prototype[method] = function(html) {
+      var elt = document.createElement(tag);
+      elt.innerHTML = typeof html === undefined ? "" : html;
+      return addElement(elt, this);
+    }
+  });
+
+  /**
+   * Creates an &lt;img /&gt; element in the DOM with given src and
+   * alternate text.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createImg
+   * @param  {String} src src path or url for image
+   * @param  {String} [alt] alternate text to be used if image does not load
+   * @param  {Function} [successCallback] callback to be called once image data is loaded
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var img;
+   * function setup() {
+   *   img = createImg('http://p5js.org/img/asterisk-01.png');
+   * }
+   * </code></div>
+   */
+  p5.prototype.createImg = function() {
+    var elt = document.createElement('img');
+    var args = arguments;
+    var self;
+    var setAttrs = function(){
+      self.width = elt.offsetWidth;
+      self.height = elt.offsetHeight;
+      if (args.length > 1 && typeof args[1] === 'function'){
+        self.fn = args[1];
+        self.fn();
+      }else if (args.length > 1 && typeof args[2] === 'function'){
+        self.fn = args[2];
+        self.fn();
+      }
+    };
+    elt.src = args[0];
+    if (args.length > 1 && typeof args[1] === 'string'){
+      elt.alt = args[1];
+    }
+    elt.onload = function(){
+      setAttrs();
+    }
+    self = addElement(elt, this);
+    return self;
+  };
+
+  /**
+   * Creates an &lt;a&gt;&lt;/a&gt; element in the DOM for including a hyperlink.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createA
+   * @param  {String} href       url of page to link to
+   * @param  {String} html       inner html of link element to display
+   * @param  {String} [target]   target where new link should open,
+   *                             could be _blank, _self, _parent, _top.
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var myLink;
+   * function setup() {
+   *   myLink = createA('http://p5js.org/', 'this is a link');
+   * }
+   * </code></div>
+   */
+  p5.prototype.createA = function(href, html, target) {
+    var elt = document.createElement('a');
+    elt.href = href;
+    elt.innerHTML = html;
+    if (target) elt.target = target;
+    return addElement(elt, this);
+  };
+
+  /** INPUT **/
+
+
+  /**
+   * Creates a slider &lt;input&gt;&lt;/input&gt; element in the DOM.
+   * Use .size() to set the display length of the slider.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createSlider
+   * @param  {Number} min minimum value of the slider
+   * @param  {Number} max maximum value of the slider
+   * @param  {Number} [value] default value of the slider
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div><code>
+   * var slider;
+   * function setup() {
+   *   slider = createSlider(0, 255, 100);
+   *   slider.position(10, 10);
+   *   slider.style('width', '80px');
+   * }
+   *
+   * function draw() {
+   *   var val = slider.value();
+   *   background(val);
+   * }
+   * </code></div>
+   */
+  p5.prototype.createSlider = function(min, max, value, step) {
+    var elt = document.createElement('input');
+    elt.type = 'range';
+    elt.min = min;
+    elt.max = max;
+    if (step) elt.step = step;
+    if (typeof(value) === "number") elt.value = value;
+    return addElement(elt, this);
+  };
+
+  /**
+   * Creates a &lt;button&gt;&lt;/button&gt; element in the DOM.
+   * Use .size() to set the display size of the button.
+   * Use .mousePressed() to specify behavior on press.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createButton
+   * @param  {String} label label displayed on the button
+   * @param  {String} [value] value of the button
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var button;
+   * function setup() {
+   *   createCanvas(100, 100);
+   *   background(0);
+   *   button = createButton('click me');
+   *   button.position(19, 19);
+   *   button.mousePressed(changeBG);
+   * }
+   *
+   * function changeBG() {
+   *   var val = random(255);
+   *   background(val);
+   * }
+   * </code></div>
+   */
+  p5.prototype.createButton = function(label, value) {
+    var elt = document.createElement('button');
+    elt.innerHTML = label;
+    elt.value = value;
+    if (value) elt.value = value;
+    return addElement(elt, this);
+  };
+
+  /**
+   * Creates a checkbox &lt;input&gt;&lt;/input&gt; element in the DOM.
+   * Calling .checked() on a checkbox returns if it is checked or not
+   *
+   * @method createCheckbox
+   * @param  {String} [label] label displayed after checkbox
+   * @param  {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var checkbox;
+   *
+   * function setup() {
+   *   checkbox = createCheckbox('label', false);
+   *   checkbox.changed(myCheckedEvent);
+   * }
+   *
+   * function myCheckedEvent() {
+   *   if (this.checked()) {
+   *     console.log("Unchecking!");
+   *   } else {
+   *     console.log("Checking!");
+   *   }
+   *
+   * </code></div>
+   */
+  p5.prototype.createCheckbox = function() {
+    var elt = document.createElement('input');
+    elt.type = 'checkbox';
+    //checkbox must be wrapped in p5.Element before label so that label appears after
+    var self = addElement(elt, this);
+    self.checked = function(){
+      if (arguments.length === 0){
+        return self.elt.checked;
+      }else if(arguments[0]){
+        self.elt.checked = true;
+      }else{
+        self.elt.checked = false;
+      }
+      return self;
+    };
+    this.value = function(val){
+      self.value = val;
+      return this;
+    };
+    if (arguments[0]){
+      var ran = Math.random().toString(36).slice(2);
+      var label = document.createElement('label');
+      elt.setAttribute('id', ran);
+      label.htmlFor = ran;
+      self.value(arguments[0]);
+      label.appendChild(document.createTextNode(arguments[0]));
+      addElement(label, this);
+    }
+    if (arguments[1]){
+      elt.checked = true;
+    }
+    return self;
+  };
+
+  /**
+   * Creates a dropdown menu &lt;select&gt;&lt;/select&gt; element in the DOM.
+   * @method createSelect
+   * @param {boolean} [multiple] [true if dropdown should support multiple selections]
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div><code>
+   * var sel;
+   *
+   * function setup() {
+   *   textAlign(CENTER);
+   *   background(200);
+   *   sel = createSelect();
+   *   sel.position(10, 10);
+   *   sel.option('pear');
+   *   sel.option('kiwi');
+   *   sel.option('grape');
+   *   sel.changed(mySelectEvent);
+   * }
+   *
+   * function mySelectEvent() {
+   *   var item = sel.value();
+   *   background(200);
+   *   text("it's a "+item+"!", 50, 50);
+   * }
+   * </code></div>
+   */
+  p5.prototype.createSelect = function(mult) {
+    var elt = document.createElement('select');
+    if (mult){
+      elt.setAttribute('multiple', 'true');
+    }
+    var self = addElement(elt, this);
+    self.option = function(name, value){
+      var opt = document.createElement('option');
+      opt.innerHTML = name;
+      if (arguments.length > 1)
+        opt.value = value;
+      else
+        opt.value = name;
+      elt.appendChild(opt);
+    };
+    self.selected = function(value){
+      var arr = [];
+      if (arguments.length > 0){
+        for (var i = 0; i < this.elt.length; i++){
+          if (value.toString() === this.elt[i].value){
+            this.elt.selectedIndex = i;
+          }
+        }
+        return this;
+      }else{
+        if (mult){
+          for (var i = 0; i < this.elt.selectedOptions.length; i++){
+            arr.push(this.elt.selectedOptions[i].value);
+          }
+          return arr;
+        }else{
+          return this.elt.value;
+        }
+      }
+    };
+    return self;
+  };
+
+  /**
+   * Creates a radio button &lt;input&gt;&lt;/input&gt; element in the DOM.
+   *
+   * @method createRadio
+   * @param  {String} [divId] the id and name of the created div and input field respectively
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   */
+  p5.prototype.createRadio = function() {
+    var radios = document.querySelectorAll("input[type=radio]");
+    var count = 0;
+    if(radios.length > 1){
+      console.log(radios,radios[0].name);
+      var length = radios.length;
+      var prev=radios[0].name;
+      var current = radios[1].name;
+      count=1;
+      for(var i = 1; i < length; i++ ){
+        current = radios[i].name;
+        if(prev != current){
+          count++;
+        }
+        prev = current;
+      }
+    }
+    else if (radios.length == 1){
+      count = 1;
+    }
+    var elt = document.createElement('div');
+    var self = addElement(elt, this);
+    var times = -1;
+    self.option = function(name, value){
+      var opt = document.createElement('input');
+      opt.type = 'radio';
+      opt.innerHTML = name;
+      if (arguments.length > 1)
+        opt.value = value;
+      else
+        opt.value = name;
+      opt.setAttribute('name',"defaultradio"+count);
+      elt.appendChild(opt);
+      if (name){
+        times++;
+        var ran = Math.random().toString(36).slice(2);
+        var label = document.createElement('label');
+        opt.setAttribute('id', "defaultradio"+count+"-"+times);
+        label.htmlFor = "defaultradio"+count+"-"+times;
+        label.appendChild(document.createTextNode(name));
+        elt.appendChild(label);
+      }
+      return opt;
+    };
+    self.selected = function(){
+      var length = this.elt.childNodes.length;
+      if(arguments[0]) {
+        for (var i = 0; i < length; i+=2){
+          if(this.elt.childNodes[i].value == arguments[0])
+            this.elt.childNodes[i].checked = true;
+        }
+        return this;
+      } else {
+        for (var i = 0; i < length; i+=2){
+          if(this.elt.childNodes[i].checked == true)
+            return this.elt.childNodes[i].value;
+        }
+      }
+    };
+    self.value = function(){
+      var length = this.elt.childNodes.length;
+      if(arguments[0]) {
+        for (var i = 0; i < length; i+=2){
+          if(this.elt.childNodes[i].value == arguments[0])
+            this.elt.childNodes[i].checked = true;
+        }
+        return this;
+      } else {
+        for (var i = 0; i < length; i+=2){
+          if(this.elt.childNodes[i].checked == true)
+            return this.elt.childNodes[i].value;
+        }
+        return "";
+      }
+    };
+    return self
+  };
+
+  /**
+   * Creates an &lt;input&gt;&lt;/input&gt; element in the DOM for text input.
+   * Use .size() to set the display length of the box.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createInput
+   * @param  {Number} [value] default value of the input box
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * function setup(){
+   *   var inp = createInput('');
+   *   inp.input(myInputEvent);
+   * }
+   *
+   * function myInputEvent(){
+   *   console.log('you are typing: ', this.value());
+   * }
+   *
+   * </code></div>
+   */
+  p5.prototype.createInput = function(value) {
+    var elt = document.createElement('input');
+    elt.type = 'text';
+    if (value) elt.value = value;
+    return addElement(elt, this);
+  };
+
+  /**
+   * Creates an &lt;input&gt;&lt;/input&gt; element in the DOM of type 'file'.
+   * This allows users to select local files for use in a sketch.
+   *
+   * @method createFileInput
+   * @param  {Function} [callback] callback function for when a file loaded
+   * @param  {String} [multiple] optional to allow multiple files selected
+   * @return {Object/p5.Element} pointer to p5.Element holding created DOM element
+   */
+  p5.prototype.createFileInput = function(callback, multiple) {
+
+    // Is the file stuff supported?
+    if (window.File && window.FileReader && window.FileList && window.Blob) {
+      // Yup, we're ok and make an input file selector
+      var elt = document.createElement('input');
+      elt.type = 'file';
+
+      // If we get a second argument that evaluates to true
+      // then we are looking for multiple files
+      if (multiple) {
+        // Anything gets the job done
+        elt.multiple = 'multiple';
+      }
+
+      // Function to handle when a file is selected
+      // We're simplifying life and assuming that we always
+      // want to load every selected file
+      function handleFileSelect(evt) {
+        // These are the files
+        var files = evt.target.files;
+        // Load each one and trigger a callback
+        for (var i = 0; i < files.length; i++) {
+          var f = files[i];
+          var reader = new FileReader();
+          function makeLoader(theFile) {
+            // Making a p5.File object
+            var p5file = new p5.File(theFile);
+            return function(e) {
+              p5file.data = e.target.result;
+              callback(p5file);
+            };
+          };
+          reader.onload = makeLoader(f);
+
+          // Text or data?
+          // This should likely be improved
+          if (f.type.indexOf('text') > -1) {
+            reader.readAsText(f);
+          } else {
+            reader.readAsDataURL(f);
+          }
+        }
+      }
+
+      // Now let's handle when a file was selected
+      elt.addEventListener('change', handleFileSelect, false);
+      return addElement(elt, this);
+    } else {
+      console.log('The File APIs are not fully supported in this browser. Cannot create element.');
+    }
+  };
+
+
+  /** VIDEO STUFF **/
+
+  function createMedia(pInst, type, src, callback) {
+    var elt = document.createElement(type);
+
+    // allow src to be empty
+    var src = src || '';
+    if (typeof src === 'string') {
+      src = [src];
+    }
+    for (var i=0; i<src.length; i++) {
+      var source = document.createElement('source');
+      source.src = src[i];
+      elt.appendChild(source);
+    }
+    if (typeof callback !== 'undefined') {
+      var callbackHandler = function() {
+        callback();
+        elt.removeEventListener('canplaythrough', callbackHandler);
+      }
+      elt.addEventListener('canplaythrough', callbackHandler);
+    }
+
+    var c = addElement(elt, pInst, true);
+    c.loadedmetadata = false;
+    // set width and height onload metadata
+    elt.addEventListener('loadedmetadata', function() {
+      c.width = elt.videoWidth;
+      c.height = elt.videoHeight;
+      c.loadedmetadata = true;
+    });
+
+    return c;
+  }
+  /**
+   * Creates an HTML5 &lt;video&gt; element in the DOM for simple playback
+   * of audio/video. Shown by default, can be hidden with .hide()
+   * and drawn into canvas using video(). Appends to the container
+   * node if one is specified, otherwise appends to body. The first parameter
+   * can be either a single string path to a video file, or an array of string
+   * paths to different formats of the same video. This is useful for ensuring
+   * that your video can play across different browsers, as each supports
+   * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
+   * page</a> for further information about supported formats.
+   *
+   * @method createVideo
+   * @param  {String|Array} src  path to a video file, or array of paths for
+   *                             supporting different browsers
+   * @param  {Object} [callback] callback function to be called upon
+   *                             'canplaythrough' event fire, that is, when the
+   *                             browser can play the media, and estimates that
+   *                             enough data has been loaded to play the media
+   *                             up to its end without having to stop for
+   *                             further buffering of content
+   * @return {Object/p5.Element} pointer to video p5.Element
+   */
+  p5.prototype.createVideo = function(src, callback) {
+    return createMedia(this, 'video', src, callback);
+  };
+
+  /** AUDIO STUFF **/
+
+  /**
+   * Creates a hidden HTML5 &lt;audio&gt; element in the DOM for simple audio
+   * playback. Appends to the container node if one is specified,
+   * otherwise appends to body. The first parameter
+   * can be either a single string path to a audio file, or an array of string
+   * paths to different formats of the same audio. This is useful for ensuring
+   * that your audio can play across different browsers, as each supports
+   * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
+   * page for further information about supported formats</a>.
+   *
+   * @method createAudio
+   * @param  {String|Array} src  path to an audio file, or array of paths for
+   *                             supporting different browsers
+   * @param  {Object} [callback] callback function to be called upon
+   *                             'canplaythrough' event fire, that is, when the
+   *                             browser can play the media, and estimates that
+   *                             enough data has been loaded to play the media
+   *                             up to its end without having to stop for
+   *                             further buffering of content
+   * @return {Object/p5.Element} pointer to audio p5.Element
+   */
+  p5.prototype.createAudio = function(src, callback) {
+    return createMedia(this, 'audio', src, callback);
+  };
+
+
+  /** CAMERA STUFF **/
+
+  p5.prototype.VIDEO = 'video';
+  p5.prototype.AUDIO = 'audio';
+
+  navigator.getUserMedia  = navigator.getUserMedia ||
+                            navigator.webkitGetUserMedia ||
+                            navigator.mozGetUserMedia ||
+                            navigator.msGetUserMedia;
+
+  /**
+   * Creates a new &lt;video&gt; element that contains the audio/video feed
+   * from a webcam. This can be drawn onto the canvas using video(). More
+   * specific properties of the stream can be passing in a Constraints object.
+   * See the
+   * <a href="http://w3c.github.io/mediacapture-main/getusermedia.html">W3C
+   * spec</a> for possible properties. Note that not all of these are supported
+   * by all browsers.
+   *
+   * @method createCapture
+   * @param  {String|Constant|Object}   type type of capture, either VIDEO or
+   *                                    AUDIO if none specified, default both,
+   *                                    or a Constraints object
+   * @param  {Function}                 callback function to be called once
+   *                                    stream has loaded
+   * @return {Object/p5.Element} capture video p5.Element
+   * @example
+   * <div class='norender'><code>
+   * var capture;
+   *
+   * function setup() {
+   *   createCanvas(480, 120);
+   *   capture = createCapture(VIDEO);
+   * }
+   *
+   * function draw() {
+   *   image(capture, 0, 0, width, width*capture.height/capture.width);
+   *   filter(INVERT);
+   * }
+   * </code></div>
+   * <div class='norender'><code>
+   * function setup() {
+   *   createCanvas(480, 120);
+   *   var constraints = {
+   *     video: {
+   *       mandatory: {
+   *         minWidth: 1280,
+   *         minHeight: 720
+   *       },
+   *       optional: [
+   *         { maxFrameRate: 10 }
+   *       ]
+   *     },
+   *     audio: true
+   *   };
+   *   createCapture(constraints, function(stream) {
+   *     console.log(stream);
+   *   });
+   * }
+   * </code></div>
+   */
+  p5.prototype.createCapture = function() {
+    var useVideo = true;
+    var useAudio = true;
+    var constraints;
+    var cb;
+    for (var i=0; i<arguments.length; i++) {
+      if (arguments[i] === p5.prototype.VIDEO) {
+        useAudio = false;
+      } else if (arguments[i] === p5.prototype.AUDIO) {
+        useVideo = false;
+      } else if (typeof arguments[i] === 'object') {
+        constraints = arguments[i];
+      } else if (typeof arguments[i] === 'function') {
+        cb = arguments[i];
+      }
+    }
+
+    if (navigator.getUserMedia) {
+      var elt = document.createElement('video');
+
+      if (!constraints) {
+        constraints = {video: useVideo, audio: useAudio};
+      }
+
+      navigator.getUserMedia(constraints, function(stream) {
+        elt.src = window.URL.createObjectURL(stream);
+        elt.onloadedmetadata = function(e) {
+          elt.play();
+          if (cb) {
+            cb(stream);
+          }
+        };
+      }, function(e) { console.log(e); });
+    } else {
+      throw 'getUserMedia not supported in this browser';
+    }
+    var c = addElement(elt, this, true);
+    c.loadedmetadata = false;
+    // set width and height onload metadata
+    elt.addEventListener('loadedmetadata', function() {
+      c.width = elt.videoWidth;
+      c.height = elt.videoHeight;
+      c.loadedmetadata = true;
+    });
+    return c;
+  };
+
+  /**
+   * Creates element with given tag in the DOM with given content.
+   * Appends to the container node if one is specified, otherwise
+   * appends to body.
+   *
+   * @method createElement
+   * @param  {String} tag tag for the new element
+   * @param  {String} [content] html content to be inserted into the element
+   * @return {Object/p5.Element} pointer to p5.Element holding created node
+   * @example
+   * <div class='norender'><code>
+   * var h2 = createElement('h2','im an h2 p5.element!');
+   * </code></div>
+   */
+  p5.prototype.createElement = function(tag, content) {
+    var elt = document.createElement(tag);
+    if (typeof content !== 'undefined') {
+      elt.innerHTML = content;
+    }
+    return addElement(elt, this);
+  };
+
+
+// =============================================================================
+//                         p5.Element additions
+// =============================================================================
+  /**
+   *
+   * Adds specified class to the element.
+   *
+   * @for p5.Element
+   * @method addClass
+   * @param  {String} class name of class to add
+   * @return {Object/p5.Element}
+   * @example
+   * <div class='norender'><code>
+   * var div = createDiv('div');
+   * div.addClass('myClass');
+   * </code></div>
+   */
+  p5.Element.prototype.addClass = function(c) {
+    if (this.elt.className) {
+      // PEND don't add class more than once
+      //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
+      //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
+      this.elt.className = this.elt.className+' '+c;
+      //}
+    } else {
+      this.elt.className = c;
+    }
+    return this;
+  }
+
+  /**
+   *
+   * Removes specified class from the element.
+   *
+   * @method removeClass
+   * @param  {String} class name of class to remove
+   * @return {Object/p5.Element}
+   */
+  p5.Element.prototype.removeClass = function(c) {
+    var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
+    this.elt.className = this.elt.className.replace(regex, '');
+    this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
+    return this;
+  }
+
+  /**
+   *
+   * Attaches the element  as a child to the parent specified.
+   * Accepts either a string ID, DOM node, or p5.Element.
+   * If no argument is specified, an array of children DOM nodes is returned.
+   *
+   * @method child
+   * @param  {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
+   *                         to add to the current element
+   * @return {p5.Element}
+   * @example
+   * <div class='norender'><code>
+   * var div0 = createDiv('this is the parent');
+   * var div1 = createDiv('this is the child');
+   * div0.child(div1); // use p5.Element
+   * </code></div>
+   * <div class='norender'><code>
+   * var div0 = createDiv('this is the parent');
+   * var div1 = createDiv('this is the child');
+   * div1.id('apples');
+   * div0.child('apples'); // use id
+   * </code></div>
+   * <div class='norender'><code>
+   * var div0 = createDiv('this is the parent');
+   * var elt = document.getElementById('myChildDiv');
+   * div0.child(elt); // use element from page
+   * </code></div>
+   */
+  p5.Element.prototype.child = function(c) {
+    if (c === null){
+      return this.elt.childNodes
+    }
+    if (typeof c === 'string') {
+      if (c[0] === '#') {
+        c = c.substring(1);
+      }
+      c = document.getElementById(c);
+    } else if (c instanceof p5.Element) {
+      c = c.elt;
+    }
+    this.elt.appendChild(c);
+    return this;
+  };
+
+  /**
+   * Centers a p5 Element either vertically, horizontally,
+   * or both, relative to its parent or according to
+   * the body if the Element has no parent. If no argument is passed
+   * the Element is aligned both vertically and horizontally.
+   *
+   * @param  {String} align       passing 'vertical', 'horizontal' aligns element accordingly
+   * @return {Object/p5.Element} pointer to p5.Element
+   * @example
+   * <div><code>
+   * function setup() {
+   *   var div = createDiv('').size(10,10);
+   *   div.style('background-color','orange');
+   *   div.center();
+   *
+   * }
+   * </code></div>
+   */
+  p5.Element.prototype.center = function(align) {
+    var style = this.elt.style.display;
+    var hidden = this.elt.style.display === 'none';
+    var parentHidden = this.parent().style.display === 'none';
+    var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
+
+    if (hidden) this.show();
+
+    this.elt.style.display = 'block';
+    this.position(0,0);
+
+    if (parentHidden) this.parent().style.display = 'block';
+
+    var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
+    var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
+    var y = pos.y;
+    var x = pos.x;
+
+    if (align === 'both' || align === undefined){
+      this.position(wOffset/2, hOffset/2);
+    }else if (align === 'horizontal'){
+      this.position(wOffset/2, y);
+    }else if (align === 'vertical'){
+      this.position(x, hOffset/2);
+    }
+
+    this.style('display', style);
+
+    if (hidden) this.hide();
+
+    if (parentHidden) this.parent().style.display = 'none';
+
+    return this;
+  };
+
+  /**
+   *
+   * If an argument is given, sets the inner HTML of the element,
+   * replacing any existing html. If no arguments are given, returns
+   * the inner HTML of the element.
+   *
+   * @for p5.Element
+   * @method html
+   * @param  {String} [html] the HTML to be placed inside the element
+   * @return {Object/p5.Element|String}
+   * @example
+   * <div class='norender'><code>
+   * var div = createDiv('').size(100,100);
+   * div.style('background-color','orange');
+   * div.html('hi');
+   * </code></div>
+   */
+  p5.Element.prototype.html = function(html) {
+    if (typeof html !== 'undefined') {
+      this.elt.innerHTML = html;
+      return this;
+    } else {
+      return this.elt.innerHTML;
+    }
+  };
+
+  /**
+   *
+   * Sets the position of the element relative to (0, 0) of the
+   * window. Essentially, sets position:absolute and left and top
+   * properties of style. If no arguments given returns the x and y position
+   * of the element in an object.
+   *
+   * @method position
+   * @param  {Number} [x] x-position relative to upper left of window
+   * @param  {Number} [y] y-position relative to upper left of window
+   * @return {Object/p5.Element}
+   * @example
+   * <div><code class='norender'>
+   * function setup() {
+   *   var cnv = createCanvas(100, 100);
+   *   // positions canvas 50px to the right and 100px
+   *   // below upper left corner of the window
+   *   cnv.position(50, 100);
+   * }
+   * </code></div>
+   */
+  p5.Element.prototype.position = function() {
+    if (arguments.length === 0){
+      return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
+    }else{
+      this.elt.style.position = 'absolute';
+      this.elt.style.left = arguments[0]+'px';
+      this.elt.style.top = arguments[1]+'px';
+      this.x = arguments[0];
+      this.y = arguments[1];
+      return this;
+    }
+  };
+
+  /* Helper method called by p5.Element.style() */
+  p5.Element.prototype._translate = function(){
+    this.elt.style.position = 'absolute';
+    // save out initial non-translate transform styling
+    var transform = '';
+    if (this.elt.style.transform) {
+      transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
+      transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
+    }
+    if (arguments.length === 2) {
+      this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)';
+    } else if (arguments.length > 2) {
+      this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
+      if (arguments.length === 3) {
+        this.elt.parentElement.style.perspective = '1000px';
+      } else {
+        this.elt.parentElement.style.perspective = arguments[3]+'px';
+      }
+    }
+    // add any extra transform styling back on end
+    this.elt.style.transform += transform;
+    return this;
+  };
+
+  /* Helper method called by p5.Element.style() */
+  p5.Element.prototype._rotate = function(){
+    // save out initial non-rotate transform styling
+    var transform = '';
+    if (this.elt.style.transform) {
+      var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
+      transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
+    }
+
+    if (arguments.length === 1){
+      this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
+    }else if (arguments.length === 2){
+      this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
+    }else if (arguments.length === 3){
+      this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
+      this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
+      this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
+    }
+    // add remaining transform back on
+    this.elt.style.transform += transform;
+    return this;
+  };
+
+  /**
+   * Sets the given style (css) property (1st arg) of the element with the
+   * given value (2nd arg). If a single argument is given, .style()
+   * returns the value of the given property; however, if the single argument
+   * is given in css syntax ('text-align:center'), .style() sets the css
+   * appropriatly. .style() also handles 2d and 3d css transforms. If
+   * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
+   * accept Numbers as values. ('translate', 10, 100, 50);
+   *
+   * @method style
+   * @param  {String} property   property to be set
+   * @param  {String|Number|p5.Color} [value]   value to assign to property
+   * @param  {String|Number} [value]   value to assign to property (rotate/translate)
+   * @param  {String|Number} [value]   value to assign to property (rotate/translate)
+   * @param  {String|Number} [value]   value to assign to property (translate)
+   * @return {String|Object/p5.Element} value of property, if no value is specified
+   * or p5.Element
+   * @example
+   * <div><code class="norender">
+   * var myDiv = createDiv("I like pandas.");
+   * myDiv.style("font-size", "18px");
+   * myDiv.style("color", "#ff0000");
+   * </code></div>
+   * <div><code class="norender">
+   * var col = color(25,23,200,50);
+   * var button = createButton("button");
+   * button.style("background-color", col);
+   * button.position(10, 10);
+   * </code></div>
+   * <div><code class="norender">
+   * var myDiv = createDiv("I like lizards.");
+   * myDiv.style("position", 20, 20);
+   * myDiv.style("rotate", 45);
+   * </code></div>
+   * <div><code class="norender">
+   * var myDiv;
+   * function setup() {
+   *   background(200);
+   *   myDiv = createDiv("I like gray.");
+   *   myDiv.position(20, 20);
+   * }
+   *
+   * function draw() {
+   *   myDiv.style("font-size", mouseX+"px");
+   * }
+   * </code></div>
+   */
+  p5.Element.prototype.style = function(prop, val) {
+    var self = this;
+
+    if (val instanceof p5.Color) {
+      val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
+    }
+
+    if (typeof val === 'undefined') {
+      if (prop.indexOf(':') === -1) {
+        var styles = window.getComputedStyle(self.elt);
+        var style = styles.getPropertyValue(prop);
+        return style;
+      } else {
+        var attrs = prop.split(';');
+        for (var i = 0; i < attrs.length; i++) {
+          var parts = attrs[i].split(':');
+          if (parts[0] && parts[1]) {
+            this.elt.style[parts[0].trim()] = parts[1].trim();
+          }
+        }
+      }
+    } else {
+      if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
+        var trans = Array.prototype.shift.apply(arguments);
+        var f = this[trans] || this['_'+trans];
+        f.apply(this, arguments);
+      } else {
+        this.elt.style[prop] = val;
+        if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
+          var numVal = val.replace(/\D+/g, '');
+          this[prop] = parseInt(numVal, 10); // pend: is this necessary?
+        }
+      }
+    }
+    return this;
+  };
+
+
+  /**
+   *
+   * Adds a new attribute or changes the value of an existing attribute
+   * on the specified element. If no value is specified, returns the
+   * value of the given attribute, or null if attribute is not set.
+   *
+   * @method attribute
+   * @param  {String} attr       attribute to set
+   * @param  {String} [value]    value to assign to attribute
+   * @return {String|Object/p5.Element} value of attribute, if no value is
+   *                             specified or p5.Element
+   * @example
+   * <div class="norender"><code>
+   * var myDiv = createDiv("I like pandas.");
+   * myDiv.attribute("align", "center");
+   * </code></div>
+   */
+  p5.Element.prototype.attribute = function(attr, value) {
+    if (typeof value === 'undefined') {
+      return this.elt.getAttribute(attr);
+    } else {
+      this.elt.setAttribute(attr, value);
+      return this;
+    }
+  };
+
+
+  /**
+   * Either returns the value of the element if no arguments
+   * given, or sets the value of the element.
+   *
+   * @method value
+   * @param  {String|Number}     [value]
+   * @return {String|Object/p5.Element} value of element if no value is specified or p5.Element
+   * @example
+   * <div class='norender'><code>
+   * // gets the value
+   * var inp;
+   * function setup() {
+   *   inp = createInput('');
+   * }
+   *
+   * function mousePressed() {
+   *   print(inp.value());
+   * }
+   * </code></div>
+   * <div class='norender'><code>
+   * // sets the value
+   * var inp;
+   * function setup() {
+   *   inp = createInput('myValue');
+   * }
+   *
+   * function mousePressed() {
+   *   inp.value("myValue");
+   * }
+   * </code></div>
+   */
+  p5.Element.prototype.value = function() {
+    if (arguments.length > 0) {
+      this.elt.value = arguments[0];
+      return this;
+    } else {
+      if (this.elt.type === 'range') {
+        return parseFloat(this.elt.value);
+      }
+      else return this.elt.value;
+    }
+  };
+
+  /**
+   *
+   * Shows the current element. Essentially, setting display:block for the style.
+   *
+   * @method show
+   * @return {Object/p5.Element}
+   * @example
+   * <div class='norender'><code>
+   * var div = createDiv('div');
+   * div.attribute("display", "none");
+   * div.show(); // turns display to block
+   * </code></div>
+   */
+  p5.Element.prototype.show = function() {
+    this.elt.style.display = 'block';
+    return this;
+  };
+
+  /**
+   * Hides the current element. Essentially, setting display:none for the style.
+   *
+   * @method hide
+   * @return {Object/p5.Element}
+   * @example
+   * <div class='norender'><code>
+   * var div = createDiv('this is a div');
+   * div.hide();
+   * </code></div>
+   */
+  p5.Element.prototype.hide = function() {
+    this.elt.style.display = 'none';
+    return this;
+  };
+
+  /**
+   *
+   * Sets the width and height of the element. AUTO can be used to
+   * only adjust one dimension. If no arguments given returns the width and height
+   * of the element in an object.
+   *
+   * @method size
+   * @param  {Number} [w] width of the element
+   * @param  {Number} [h] height of the element
+   * @return {Object/p5.Element}
+   * @example
+   * <div class='norender'><code>
+   * var div = createDiv('this is a div');
+   * div.size(100, 100);
+   * </code></div>
+   */
+  p5.Element.prototype.size = function(w, h) {
+    if (arguments.length === 0){
+      return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
+    }else{
+      var aW = w;
+      var aH = h;
+      var AUTO = p5.prototype.AUTO;
+      if (aW !== AUTO || aH !== AUTO) {
+        if (aW === AUTO) {
+          aW = h * this.width / this.height;
+        } else if (aH === AUTO) {
+          aH = w * this.height / this.width;
+        }
+        // set diff for cnv vs normal div
+        if (this.elt instanceof HTMLCanvasElement) {
+          var j = {};
+          var k  = this.elt.getContext('2d');
+          for (var prop in k) {
+            j[prop] = k[prop];
+          }
+          this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
+          this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
+          this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
+          this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
+          for (var prop in j) {
+            this.elt.getContext('2d')[prop] = j[prop];
+          }
+        } else {
+          this.elt.style.width = aW+'px';
+          this.elt.style.height = aH+'px';
+          this.elt.width = aW;
+          this.elt.height = aH;
+          this.width = aW;
+          this.height = aH;
+        }
+
+        this.width = this.elt.offsetWidth;
+        this.height = this.elt.offsetHeight;
+
+        if (this._pInst) { // main canvas associated with p5 instance
+          if (this._pInst._curElement.elt === this.elt) {
+            this._pInst._setProperty('width', this.elt.offsetWidth);
+            this._pInst._setProperty('height', this.elt.offsetHeight);
+          }
+        }
+      }
+      return this;
+    }
+  };
+
+  /**
+   * Removes the element and deregisters all listeners.
+   * @method remove
+   * @example
+   * <div class='norender'><code>
+   * var myDiv = createDiv('this is some text');
+   * myDiv.remove();
+   * </code></div>
+   */
+  p5.Element.prototype.remove = function() {
+    // deregister events
+    for (var ev in this._events) {
+      this.elt.removeEventListener(ev, this._events[ev]);
+    }
+    if (this.elt.parentNode) {
+      this.elt.parentNode.removeChild(this.elt);
+    }
+    delete(this);
+  };
+
+
+
+// =============================================================================
+//                         p5.MediaElement additions
+// =============================================================================
+
+
+  /**
+   * Extends p5.Element to handle audio and video. In addition to the methods
+   * of p5.Element, it also contains methods for controlling media. It is not
+   * called directly, but p5.MediaElements are created by calling createVideo,
+   * createAudio, and createCapture.
+   *
+   * @class p5.MediaElement
+   * @constructor
+   * @param {String} elt DOM node that is wrapped
+   * @param {Object} [pInst] pointer to p5 instance
+   */
+  p5.MediaElement = function(elt, pInst) {
+    p5.Element.call(this, elt, pInst);
+
+    var self = this;
+    this.elt.crossOrigin = 'anonymous';
+
+    this._prevTime = 0;
+    this._cueIDCounter = 0;
+    this._cues = [];
+    this._pixelDensity = 1;
+
+    /**
+     *  Path to the media element source.
+     *
+     *  @property src
+     *  @return {String} src
+     */
+    Object.defineProperty(self, 'src', {
+      get: function() {
+        var firstChildSrc = self.elt.children[0].src;
+        var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
+        var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
+        return ret;
+      },
+      set: function(newValue) {
+        for (var i = 0; i < self.elt.children.length; i++) {
+          self.elt.removeChild(self.elt.children[i]);
+        }
+        var source = document.createElement('source');
+        source.src = newValue;
+        elt.appendChild(source);
+        self.elt.src = newValue;
+      },
+    });
+
+    // private _onended callback, set by the method: onended(callback)
+    self._onended = function() {};
+    self.elt.onended = function() {
+      self._onended(self);
+    }
+  };
+  p5.MediaElement.prototype = Object.create(p5.Element.prototype);
+
+
+
+
+  /**
+   * Play an HTML5 media element.
+   *
+   * @method play
+   * @return {Object/p5.Element}
+   */
+  p5.MediaElement.prototype.play = function() {
+    if (this.elt.currentTime === this.elt.duration) {
+      this.elt.currentTime = 0;
+    }
+
+    if (this.elt.readyState > 1) {
+      this.elt.play();
+    } else {
+      // in Chrome, playback cannot resume after being stopped and must reload
+      this.elt.load();
+      this.elt.play();
+    }
+    return this;
+  };
+
+  /**
+   * Stops an HTML5 media element (sets current time to zero).
+   *
+   * @method stop
+   * @return {Object/p5.Element}
+   */
+  p5.MediaElement.prototype.stop = function() {
+    this.elt.pause();
+    this.elt.currentTime = 0;
+    return this;
+  };
+
+  /**
+   * Pauses an HTML5 media element.
+   *
+   * @method pause
+   * @return {Object/p5.Element}
+   */
+  p5.MediaElement.prototype.pause = function() {
+    this.elt.pause();
+    return this;
+  };
+
+  /**
+   * Set 'loop' to true for an HTML5 media element, and starts playing.
+   *
+   * @method loop
+   * @return {Object/p5.Element}
+   */
+  p5.MediaElement.prototype.loop = function() {
+    this.elt.setAttribute('loop', true);
+    this.play();
+    return this;
+  };
+  /**
+   * Set 'loop' to false for an HTML5 media element. Element will stop
+   * when it reaches the end.
+   *
+   * @method noLoop
+   * @return {Object/p5.Element}
+   */
+  p5.MediaElement.prototype.noLoop = function() {
+    this.elt.setAttribute('loop', false);
+    return this;
+  };
+
+
+  /**
+   * Set HTML5 media element to autoplay or not.
+   *
+   * @method autoplay
+   * @param {Boolean} autoplay whether the element should autoplay
+   * @return {Object/p5.Element}
+   */
+  p5.MediaElement.prototype.autoplay = function(val) {
+    this.elt.setAttribute('autoplay', val);
+    return this;
+  };
+
+  /**
+   * Sets volume for this HTML5 media element. If no argument is given,
+   * returns the current volume.
+   *
+   * @param {Number}            [val] volume between 0.0 and 1.0
+   * @return {Number|p5.MediaElement} current volume or p5.MediaElement
+   * @method volume
+   */
+  p5.MediaElement.prototype.volume = function(val) {
+    if (typeof val === 'undefined') {
+      return this.elt.volume;
+    } else {
+      this.elt.volume = val;
+    }
+  };
+
+  /**
+   * If no arguments are given, returns the current playback speed of the
+   * element. The speed parameter sets the speed where 2.0 will play the
+   * element twice as fast, 0.5 will play at half the speed, and -1 will play
+   * the element in normal speed in reverse.(Note that not all browsers support
+   * backward playback and even if they do, playback might not be smooth.)
+   *
+   * @method speed
+   * @param {Number} [speed]  speed multiplier for element playback
+   * @return {Number|Object/p5.MediaElement} current playback speed or p5.MediaElement
+   */
+  p5.MediaElement.prototype.speed = function(val) {
+    if (typeof val === 'undefined') {
+      return this.elt.playbackRate;
+    } else {
+      this.elt.playbackRate = val;
+    }
+  };
+
+  /**
+   * If no arguments are given, returns the current time of the element.
+   * If an argument is given the current time of the element is set to it.
+   *
+   * @method time
+   * @param {Number} [time] time to jump to (in seconds)
+   * @return {Number|Object/p5.MediaElement} current time (in seconds)
+   *                                  or p5.MediaElement
+   */
+  p5.MediaElement.prototype.time = function(val) {
+    if (typeof val === 'undefined') {
+      return this.elt.currentTime;
+    } else {
+      this.elt.currentTime = val;
+    }
+  };
+
+  /**
+   * Returns the duration of the HTML5 media element.
+   *
+   * @method duration
+   * @return {Number} duration
+   */
+  p5.MediaElement.prototype.duration = function() {
+    return this.elt.duration;
+  };
+  p5.MediaElement.prototype.pixels = [];
+  p5.MediaElement.prototype.loadPixels = function() {
+    if (!this.canvas) {
+      this.canvas = document.createElement('canvas');
+      this.drawingContext = this.canvas.getContext('2d');
+    }
+    if (this.loadedmetadata) { // wait for metadata for w/h
+      if (this.canvas.width !== this.elt.videoWidth) {
+        this.canvas.width = this.elt.videoWidth;
+        this.canvas.height = this.elt.videoHeight;
+        this.width = this.canvas.width;
+        this.height = this.canvas.height;
+      }
+      this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
+      p5.Renderer2D.prototype.loadPixels.call(this);
+    }
+    return this;
+  }
+  p5.MediaElement.prototype.updatePixels =  function(x, y, w, h){
+    if (this.loadedmetadata) { // wait for metadata
+      p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
+    }
+    return this;
+  }
+  p5.MediaElement.prototype.get = function(x, y, w, h){
+    if (this.loadedmetadata) { // wait for metadata
+      return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
+    } else return [0, 0, 0, 255];
+  };
+  p5.MediaElement.prototype.set = function(x, y, imgOrCol){
+    if (this.loadedmetadata) { // wait for metadata
+      p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
+    }
+  };
+  /**
+   *  Schedule an event to be called when the audio or video
+   *  element reaches the end. If the element is looping,
+   *  this will not be called. The element is passed in
+   *  as the argument to the onended callback.
+   *
+   *  @method  onended
+   *  @param  {Function} callback function to call when the
+   *                              soundfile has ended. The
+   *                              media element will be passed
+   *                              in as the argument to the
+   *                              callback.
+   *  @return {Object/p5.MediaElement}
+   *  @example
+   *  <div><code>
+   *  function setup() {
+   *    audioEl = createAudio('assets/beat.mp3');
+   *    audioEl.showControls(true);
+   *    audioEl.onended(sayDone);
+   *  }
+   *
+   *  function sayDone(elt) {
+   *    alert('done playing ' + elt.src );
+   *  }
+   *  </code></div>
+   */
+  p5.MediaElement.prototype.onended = function(callback) {
+    this._onended = callback;
+    return this;
+  };
+
+
+  /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
+
+  /**
+   *  Send the audio output of this element to a specified audioNode or
+   *  p5.sound object. If no element is provided, connects to p5's master
+   *  output. That connection is established when this method is first called.
+   *  All connections are removed by the .disconnect() method.
+   *
+   *  This method is meant to be used with the p5.sound.js addon library.
+   *
+   *  @method  connect
+   *  @param  {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
+   *  or an object from the p5.sound library
+   */
+  p5.MediaElement.prototype.connect = function(obj) {
+    var audioContext, masterOutput;
+
+    // if p5.sound exists, same audio context
+    if (typeof p5.prototype.getAudioContext === 'function') {
+      audioContext = p5.prototype.getAudioContext();
+      masterOutput = p5.soundOut.input;
+    } else {
+      try {
+        audioContext = obj.context;
+        masterOutput = audioContext.destination
+      } catch(e) {
+        throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
+      }
+    }
+
+    // create a Web Audio MediaElementAudioSourceNode if none already exists
+    if (!this.audioSourceNode) {
+      this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
+
+      // connect to master output when this method is first called
+      this.audioSourceNode.connect(masterOutput);
+    }
+
+    // connect to object if provided
+    if (obj) {
+      if (obj.input) {
+        this.audioSourceNode.connect(obj.input);
+      } else {
+        this.audioSourceNode.connect(obj);
+      }
+    }
+
+    // otherwise connect to master output of p5.sound / AudioContext
+    else {
+      this.audioSourceNode.connect(masterOutput);
+    }
+
+  };
+
+  /**
+   *  Disconnect all Web Audio routing, including to master output.
+   *  This is useful if you want to re-route the output through
+   *  audio effects, for example.
+   *
+   *  @method  disconnect
+   */
+  p5.MediaElement.prototype.disconnect = function() {
+    if (this.audioSourceNode) {
+      this.audioSourceNode.disconnect();
+    } else {
+      throw 'nothing to disconnect';
+    }
+  };
+
+
+  /*** SHOW / HIDE CONTROLS ***/
+
+  /**
+   *  Show the default MediaElement controls, as determined by the web browser.
+   *
+   *  @method  showControls
+   */
+  p5.MediaElement.prototype.showControls = function() {
+    // must set style for the element to show on the page
+    this.elt.style['text-align'] = 'inherit';
+    this.elt.controls = true;
+  };
+
+  /**
+   *  Hide the default mediaElement controls.
+   *
+   *  @method hideControls
+   */
+  p5.MediaElement.prototype.hideControls = function() {
+    this.elt.controls = false;
+  };
+
+  /*** SCHEDULE EVENTS ***/
+
+  /**
+   *  Schedule events to trigger every time a MediaElement
+   *  (audio/video) reaches a playback cue point.
+   *
+   *  Accepts a callback function, a time (in seconds) at which to trigger
+   *  the callback, and an optional parameter for the callback.
+   *
+   *  Time will be passed as the first parameter to the callback function,
+   *  and param will be the second parameter.
+   *
+   *
+   *  @method  addCue
+   *  @param {Number}   time     Time in seconds, relative to this media
+   *                             element's playback. For example, to trigger
+   *                             an event every time playback reaches two
+   *                             seconds, pass in the number 2. This will be
+   *                             passed as the first parameter to
+   *                             the callback function.
+   *  @param {Function} callback Name of a function that will be
+   *                             called at the given time. The callback will
+   *                             receive time and (optionally) param as its
+   *                             two parameters.
+   *  @param {Object} [value]    An object to be passed as the
+   *                             second parameter to the
+   *                             callback function.
+   *  @return {Number} id ID of this cue,
+   *                      useful for removeCue(id)
+   *  @example
+   *  <div><code>
+   *  function setup() {
+   *    background(255,255,255);
+   *
+   *    audioEl = createAudio('assets/beat.mp3');
+   *    audioEl.showControls();
+   *
+   *    // schedule three calls to changeBackground
+   *    audioEl.addCue(0.5, changeBackground, color(255,0,0) );
+   *    audioEl.addCue(1.0, changeBackground, color(0,255,0) );
+   *    audioEl.addCue(2.5, changeBackground, color(0,0,255) );
+   *    audioEl.addCue(3.0, changeBackground, color(0,255,255) );
+   *    audioEl.addCue(4.2, changeBackground, color(255,255,0) );
+   *    audioEl.addCue(5.0, changeBackground, color(255,255,0) );
+   *  }
+   *
+   *  function changeBackground(val) {
+   *    background(val);
+   *  }
+   *  </code></div>
+   */
+  p5.MediaElement.prototype.addCue = function(time, callback, val) {
+    var id = this._cueIDCounter++;
+
+    var cue = new Cue(callback, time, id, val);
+    this._cues.push(cue);
+
+    if (!this.elt.ontimeupdate) {
+      this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
+    }
+
+    return id;
+  };
+
+  /**
+   *  Remove a callback based on its ID. The ID is returned by the
+   *  addCue method.
+   *
+   *  @method removeCue
+   *  @param  {Number} id ID of the cue, as returned by addCue
+   */
+  p5.MediaElement.prototype.removeCue = function(id) {
+    for (var i = 0; i < this._cues.length; i++) {
+      var cue = this._cues[i];
+      if (cue.id === id) {
+        this.cues.splice(i, 1);
+      }
+    }
+
+    if (this._cues.length === 0) {
+      this.elt.ontimeupdate = null
+    }
+  };
+
+  /**
+   *  Remove all of the callbacks that had originally been scheduled
+   *  via the addCue method.
+   *
+   *  @method  clearCues
+   */
+  p5.MediaElement.prototype.clearCues = function() {
+    this._cues = [];
+    this.elt.ontimeupdate = null;
+  };
+
+  // private method that checks for cues to be fired if events
+  // have been scheduled using addCue(callback, time).
+  p5.MediaElement.prototype._onTimeUpdate = function() {
+    var playbackTime = this.time();
+
+    for (var i = 0 ; i < this._cues.length; i++) {
+      var callbackTime = this._cues[i].time;
+      var val = this._cues[i].val;
+
+
+      if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
+
+        // pass the scheduled callbackTime as parameter to the callback
+        this._cues[i].callback(val);
+      }
+
+    }
+
+    this._prevTime = playbackTime;
+  };
+
+
+  // Cue inspired by JavaScript setTimeout, and the
+  // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
+  var Cue = function(callback, time, id, val) {
+    this.callback = callback;
+    this.time = time;
+    this.id = id;
+    this.val = val;
+  };
+
+// =============================================================================
+//                         p5.File
+// =============================================================================
+
+
+  /**
+   * Base class for a file
+   * Using this for createFileInput
+   *
+   * @class p5.File
+   * @constructor
+   * @param {File} file File that is wrapped
+   * @param {Object} [pInst] pointer to p5 instance
+   */
+  p5.File = function(file, pInst) {
+    /**
+     * Underlying File object. All normal File methods can be called on this.
+     *
+     * @property file
+     */
+    this.file = file;
+
+    this._pInst = pInst;
+
+    // Splitting out the file type into two components
+    // This makes determining if image or text etc simpler
+    var typeList = file.type.split('/');
+    /**
+     * File type (image, text, etc.)
+     *
+     * @property type
+     */
+    this.type = typeList[0];
+    /**
+     * File subtype (usually the file extension jpg, png, xml, etc.)
+     *
+     * @property subtype
+     */
+    this.subtype = typeList[1];
+    /**
+     * File name
+     *
+     * @property name
+     */
+    this.name = file.name;
+    /**
+     * File size
+     *
+     * @property size
+     */
+    this.size = file.size;
+
+    /**
+     * URL string containing image data.
+     *
+     * @property data
+     */
+    this.data = undefined;
+  };
+
+}));
diff --git a/examples/p5js/libraries/p5.js b/examples/p5js/libraries/p5.js
new file mode 100644
index 0000000000000000000000000000000000000000..43d7136f8cbe364d0ec9e256f2971bf19d12e774
--- /dev/null
+++ b/examples/p5js/libraries/p5.js
@@ -0,0 +1,29765 @@
+/*! p5.js v0.4.23 March 04, 2016 */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.p5 = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+
+},{}],2:[function(_dereq_,module,exports){
+// Run-time checking of preconditions.
+
+'use strict';
+
+// Precondition function that checks if the given predicate is true.
+// If not, it will throw an error.
+exports.argument = function(predicate, message) {
+    if (!predicate) {
+        throw new Error(message);
+    }
+};
+
+// Precondition function that checks if the given assertion is true.
+// If not, it will throw an error.
+exports.assert = exports.argument;
+
+},{}],3:[function(_dereq_,module,exports){
+// Drawing utility functions.
+
+'use strict';
+
+// Draw a line on the given context from point `x1,y1` to point `x2,y2`.
+function line(ctx, x1, y1, x2, y2) {
+    ctx.beginPath();
+    ctx.moveTo(x1, y1);
+    ctx.lineTo(x2, y2);
+    ctx.stroke();
+}
+
+exports.line = line;
+
+},{}],4:[function(_dereq_,module,exports){
+// Glyph encoding
+
+'use strict';
+
+var cffStandardStrings = [
+    '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
+    'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two',
+    'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater',
+    'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
+    'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+    'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling',
+    'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
+    'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph',
+    'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
+    'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring',
+    'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE',
+    'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu',
+    'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn',
+    'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright',
+    'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex',
+    'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex',
+    'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute',
+    'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute',
+    'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute',
+    'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
+    'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior',
+    'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', '266 ff', 'onedotenleader',
+    'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle',
+    'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
+    'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
+    'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl',
+    'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall',
+    'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall',
+    'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
+    'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall',
+    'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall',
+    'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
+    'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds',
+    'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
+    'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior',
+    'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior',
+    'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall',
+    'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall',
+    'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall',
+    'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall',
+    'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000',
+    '001.001', '001.002', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'];
+
+var cffStandardEncoding = [
+    '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+    '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
+    'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two',
+    'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater',
+    'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
+    'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+    'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', '', '', '', '', '', '', '', '',
+    '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+    'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
+    'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger',
+    'daggerdbl', 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright',
+    'guillemotright', 'ellipsis', 'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex', 'tilde',
+    'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron',
+    'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'AE', '', 'ordfeminine', '', '', '',
+    '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '',
+    'lslash', 'oslash', 'oe', 'germandbls'];
+
+var cffExpertEncoding = [
+    '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+    '', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', '', 'dollaroldstyle', 'dollarsuperior',
+    'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
+    'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
+    'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon',
+    'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', '', 'asuperior',
+    'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', '', '', 'isuperior', '', '', 'lsuperior', 'msuperior',
+    'nsuperior', 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior', '', 'ff', 'fi', 'fl', 'ffi', 'ffl',
+    'parenleftinferior', '', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall',
+    'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall',
+    'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
+    'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', '', '', '', '', '', '', '',
+    '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+    'exclamdownsmall', 'centoldstyle', 'Lslashsmall', '', '', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall',
+    'Brevesmall', 'Caronsmall', '', 'Dotaccentsmall', '', '', 'Macronsmall', '', '', 'figuredash', 'hypheninferior',
+    '', '', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters',
+    'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', '',
+    '', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
+    'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior',
+    'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior',
+    'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
+    'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall',
+    'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall',
+    'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
+    'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall',
+    'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'];
+
+var standardNames = [
+    '.notdef', '.null', 'nonmarkingreturn', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
+    'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash',
+    'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less',
+    'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
+    'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde',
+    'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave',
+    'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis',
+    'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis',
+    'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section',
+    'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute', 'dieresis', 'notequal',
+    'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation',
+    'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown',
+    'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright',
+    'ellipsis', 'nonbreakingspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft',
+    'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction',
+    'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase',
+    'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute',
+    'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex',
+    'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut',
+    'ogonek', 'caron', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Zcaron', 'zcaron', 'brokenbar', 'Eth', 'eth',
+    'Yacute', 'yacute', 'Thorn', 'thorn', 'minus', 'multiply', 'onesuperior', 'twosuperior', 'threesuperior',
+    'onehalf', 'onequarter', 'threequarters', 'franc', 'Gbreve', 'gbreve', 'Idotaccent', 'Scedilla', 'scedilla',
+    'Cacute', 'cacute', 'Ccaron', 'ccaron', 'dcroat'];
+
+// This is the encoding used for fonts created from scratch.
+// It loops through all glyphs and finds the appropriate unicode value.
+// Since it's linear time, other encodings will be faster.
+function DefaultEncoding(font) {
+    this.font = font;
+}
+
+DefaultEncoding.prototype.charToGlyphIndex = function(c) {
+    var code = c.charCodeAt(0);
+    var glyphs = this.font.glyphs;
+    if (glyphs) {
+        for (var i = 0; i < glyphs.length; i += 1) {
+            var glyph = glyphs.get(i);
+            for (var j = 0; j < glyph.unicodes.length; j += 1) {
+                if (glyph.unicodes[j] === code) {
+                    return i;
+                }
+            }
+        }
+    } else {
+        return null;
+    }
+};
+
+function CmapEncoding(cmap) {
+    this.cmap = cmap;
+}
+
+CmapEncoding.prototype.charToGlyphIndex = function(c) {
+    return this.cmap.glyphIndexMap[c.charCodeAt(0)] || 0;
+};
+
+function CffEncoding(encoding, charset) {
+    this.encoding = encoding;
+    this.charset = charset;
+}
+
+CffEncoding.prototype.charToGlyphIndex = function(s) {
+    var code = s.charCodeAt(0);
+    var charName = this.encoding[code];
+    return this.charset.indexOf(charName);
+};
+
+function GlyphNames(post) {
+    var i;
+    switch (post.version) {
+    case 1:
+        this.names = exports.standardNames.slice();
+        break;
+    case 2:
+        this.names = new Array(post.numberOfGlyphs);
+        for (i = 0; i < post.numberOfGlyphs; i++) {
+            if (post.glyphNameIndex[i] < exports.standardNames.length) {
+                this.names[i] = exports.standardNames[post.glyphNameIndex[i]];
+            } else {
+                this.names[i] = post.names[post.glyphNameIndex[i] - exports.standardNames.length];
+            }
+        }
+
+        break;
+    case 2.5:
+        this.names = new Array(post.numberOfGlyphs);
+        for (i = 0; i < post.numberOfGlyphs; i++) {
+            this.names[i] = exports.standardNames[i + post.glyphNameIndex[i]];
+        }
+
+        break;
+    case 3:
+        this.names = [];
+        break;
+    }
+}
+
+GlyphNames.prototype.nameToGlyphIndex = function(name) {
+    return this.names.indexOf(name);
+};
+
+GlyphNames.prototype.glyphIndexToName = function(gid) {
+    return this.names[gid];
+};
+
+function addGlyphNames(font) {
+    var glyph;
+    var glyphIndexMap = font.tables.cmap.glyphIndexMap;
+    var charCodes = Object.keys(glyphIndexMap);
+
+    for (var i = 0; i < charCodes.length; i += 1) {
+        var c = charCodes[i];
+        var glyphIndex = glyphIndexMap[c];
+        glyph = font.glyphs.get(glyphIndex);
+        glyph.addUnicode(parseInt(c));
+    }
+
+    for (i = 0; i < font.glyphs.length; i += 1) {
+        glyph = font.glyphs.get(i);
+        if (font.cffEncoding) {
+            glyph.name = font.cffEncoding.charset[i];
+        } else {
+            glyph.name = font.glyphNames.glyphIndexToName(i);
+        }
+    }
+}
+
+exports.cffStandardStrings = cffStandardStrings;
+exports.cffStandardEncoding = cffStandardEncoding;
+exports.cffExpertEncoding = cffExpertEncoding;
+exports.standardNames = standardNames;
+exports.DefaultEncoding = DefaultEncoding;
+exports.CmapEncoding = CmapEncoding;
+exports.CffEncoding = CffEncoding;
+exports.GlyphNames = GlyphNames;
+exports.addGlyphNames = addGlyphNames;
+
+},{}],5:[function(_dereq_,module,exports){
+// The Font object
+
+'use strict';
+
+var path = _dereq_('./path');
+var sfnt = _dereq_('./tables/sfnt');
+var encoding = _dereq_('./encoding');
+var glyphset = _dereq_('./glyphset');
+
+// A Font represents a loaded OpenType font file.
+// It contains a set of glyphs and methods to draw text on a drawing context,
+// or to get a path representing the text.
+function Font(options) {
+    options = options || {};
+
+    // OS X will complain if the names are empty, so we put a single space everywhere by default.
+    this.familyName = options.familyName || ' ';
+    this.styleName = options.styleName || ' ';
+    this.designer = options.designer || ' ';
+    this.designerURL = options.designerURL || ' ';
+    this.manufacturer = options.manufacturer || ' ';
+    this.manufacturerURL = options.manufacturerURL || ' ';
+    this.license = options.license || ' ';
+    this.licenseURL = options.licenseURL || ' ';
+    this.version = options.version || 'Version 0.1';
+    this.description = options.description || ' ';
+    this.copyright = options.copyright || ' ';
+    this.trademark = options.trademark || ' ';
+    this.unitsPerEm = options.unitsPerEm || 1000;
+    this.ascender = options.ascender;
+    this.descender = options.descender;
+    this.supported = true;
+    this.glyphs = new glyphset.GlyphSet(this, options.glyphs || []);
+    this.encoding = new encoding.DefaultEncoding(this);
+    this.tables = {};
+}
+
+// Check if the font has a glyph for the given character.
+Font.prototype.hasChar = function(c) {
+    return this.encoding.charToGlyphIndex(c) !== null;
+};
+
+// Convert the given character to a single glyph index.
+// Note that this function assumes that there is a one-to-one mapping between
+// the given character and a glyph; for complex scripts this might not be the case.
+Font.prototype.charToGlyphIndex = function(s) {
+    return this.encoding.charToGlyphIndex(s);
+};
+
+// Convert the given character to a single Glyph object.
+// Note that this function assumes that there is a one-to-one mapping between
+// the given character and a glyph; for complex scripts this might not be the case.
+Font.prototype.charToGlyph = function(c) {
+    var glyphIndex = this.charToGlyphIndex(c);
+    var glyph = this.glyphs.get(glyphIndex);
+    if (!glyph) {
+        // .notdef
+        glyph = this.glyphs.get(0);
+    }
+
+    return glyph;
+};
+
+// Convert the given text to a list of Glyph objects.
+// Note that there is no strict one-to-one mapping between characters and
+// glyphs, so the list of returned glyphs can be larger or smaller than the
+// length of the given string.
+Font.prototype.stringToGlyphs = function(s) {
+    var glyphs = [];
+    for (var i = 0; i < s.length; i += 1) {
+        var c = s[i];
+        glyphs.push(this.charToGlyph(c));
+    }
+
+    return glyphs;
+};
+
+Font.prototype.nameToGlyphIndex = function(name) {
+    return this.glyphNames.nameToGlyphIndex(name);
+};
+
+Font.prototype.nameToGlyph = function(name) {
+    var glyphIndex = this.nametoGlyphIndex(name);
+    var glyph = this.glyphs.get(glyphIndex);
+    if (!glyph) {
+        // .notdef
+        glyph = this.glyphs.get(0);
+    }
+
+    return glyph;
+};
+
+Font.prototype.glyphIndexToName = function(gid) {
+    if (!this.glyphNames.glyphIndexToName) {
+        return '';
+    }
+
+    return this.glyphNames.glyphIndexToName(gid);
+};
+
+// Retrieve the value of the kerning pair between the left glyph (or its index)
+// and the right glyph (or its index). If no kerning pair is found, return 0.
+// The kerning value gets added to the advance width when calculating the spacing
+// between glyphs.
+Font.prototype.getKerningValue = function(leftGlyph, rightGlyph) {
+    leftGlyph = leftGlyph.index || leftGlyph;
+    rightGlyph = rightGlyph.index || rightGlyph;
+    var gposKerning = this.getGposKerningValue;
+    return gposKerning ? gposKerning(leftGlyph, rightGlyph) :
+        (this.kerningPairs[leftGlyph + ',' + rightGlyph] || 0);
+};
+
+// Helper function that invokes the given callback for each glyph in the given text.
+// The callback gets `(glyph, x, y, fontSize, options)`.
+Font.prototype.forEachGlyph = function(text, x, y, fontSize, options, callback) {
+    if (!this.supported) {
+        return;
+    }
+
+    x = x !== undefined ? x : 0;
+    y = y !== undefined ? y : 0;
+    fontSize = fontSize !== undefined ? fontSize : 72;
+    options = options || {};
+    var kerning = options.kerning === undefined ? true : options.kerning;
+    var fontScale = 1 / this.unitsPerEm * fontSize;
+    var glyphs = this.stringToGlyphs(text);
+    for (var i = 0; i < glyphs.length; i += 1) {
+        var glyph = glyphs[i];
+        callback(glyph, x, y, fontSize, options);
+        if (glyph.advanceWidth) {
+            x += glyph.advanceWidth * fontScale;
+        }
+
+        if (kerning && i < glyphs.length - 1) {
+            var kerningValue = this.getKerningValue(glyph, glyphs[i + 1]);
+            x += kerningValue * fontScale;
+        }
+    }
+};
+
+// Create a Path object that represents the given text.
+//
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+//
+// Returns a Path object.
+Font.prototype.getPath = function(text, x, y, fontSize, options) {
+    var fullPath = new path.Path();
+    this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
+        var glyphPath = glyph.getPath(gX, gY, gFontSize);
+        fullPath.extend(glyphPath);
+    });
+
+    return fullPath;
+};
+
+// Draw the text on the given drawing context.
+//
+// ctx - A 2D drawing context, like Canvas.
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+Font.prototype.draw = function(ctx, text, x, y, fontSize, options) {
+    this.getPath(text, x, y, fontSize, options).draw(ctx);
+};
+
+// Draw the points of all glyphs in the text.
+// On-curve points will be drawn in blue, off-curve points will be drawn in red.
+//
+// ctx - A 2D drawing context, like Canvas.
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+Font.prototype.drawPoints = function(ctx, text, x, y, fontSize, options) {
+    this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
+        glyph.drawPoints(ctx, gX, gY, gFontSize);
+    });
+};
+
+// Draw lines indicating important font measurements for all glyphs in the text.
+// Black lines indicate the origin of the coordinate system (point 0,0).
+// Blue lines indicate the glyph bounding box.
+// Green line indicates the advance width of the glyph.
+//
+// ctx - A 2D drawing context, like Canvas.
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+Font.prototype.drawMetrics = function(ctx, text, x, y, fontSize, options) {
+    this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
+        glyph.drawMetrics(ctx, gX, gY, gFontSize);
+    });
+};
+
+// Validate
+Font.prototype.validate = function() {
+    var warnings = [];
+    var _this = this;
+
+    function assert(predicate, message) {
+        if (!predicate) {
+            warnings.push(message);
+        }
+    }
+
+    function assertStringAttribute(attrName) {
+        assert(_this[attrName] && _this[attrName].trim().length > 0, 'No ' + attrName + ' specified.');
+    }
+
+    // Identification information
+    assertStringAttribute('familyName');
+    assertStringAttribute('weightName');
+    assertStringAttribute('manufacturer');
+    assertStringAttribute('copyright');
+    assertStringAttribute('version');
+
+    // Dimension information
+    assert(this.unitsPerEm > 0, 'No unitsPerEm specified.');
+};
+
+// Convert the font object to a SFNT data structure.
+// This structure contains all the necessary tables and metadata to create a binary OTF file.
+Font.prototype.toTables = function() {
+    return sfnt.fontToTable(this);
+};
+
+Font.prototype.toBuffer = function() {
+    var sfntTable = this.toTables();
+    var bytes = sfntTable.encode();
+    var buffer = new ArrayBuffer(bytes.length);
+    var intArray = new Uint8Array(buffer);
+    for (var i = 0; i < bytes.length; i++) {
+        intArray[i] = bytes[i];
+    }
+
+    return buffer;
+};
+
+// Initiate a download of the OpenType font.
+Font.prototype.download = function() {
+    var fileName = this.familyName.replace(/\s/g, '') + '-' + this.styleName + '.otf';
+    var buffer = this.toBuffer();
+
+    window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
+    window.requestFileSystem(window.TEMPORARY, buffer.byteLength, function(fs) {
+        fs.root.getFile(fileName, {create: true}, function(fileEntry) {
+            fileEntry.createWriter(function(writer) {
+                var dataView = new DataView(buffer);
+                var blob = new Blob([dataView], {type: 'font/opentype'});
+                writer.write(blob);
+
+                writer.addEventListener('writeend', function() {
+                    // Navigating to the file will download it.
+                    location.href = fileEntry.toURL();
+                }, false);
+            });
+        });
+    },
+
+    function(err) {
+        throw err;
+    });
+};
+
+exports.Font = Font;
+
+},{"./encoding":4,"./glyphset":7,"./path":10,"./tables/sfnt":25}],6:[function(_dereq_,module,exports){
+// The Glyph object
+
+'use strict';
+
+var check = _dereq_('./check');
+var draw = _dereq_('./draw');
+var path = _dereq_('./path');
+
+function getPathDefinition(glyph, path) {
+    var _path = path || { commands: [] };
+    return {
+        configurable: true,
+
+        get: function() {
+            if (typeof _path === 'function') {
+                _path = _path();
+            }
+
+            return _path;
+        },
+
+        set: function(p) {
+            _path = p;
+        }
+    };
+}
+
+// A Glyph is an individual mark that often corresponds to a character.
+// Some glyphs, such as ligatures, are a combination of many characters.
+// Glyphs are the basic building blocks of a font.
+//
+// The `Glyph` class contains utility methods for drawing the path and its points.
+function Glyph(options) {
+    // By putting all the code on a prototype function (which is only declared once)
+    // we reduce the memory requirements for larger fonts by some 2%
+    this.bindConstructorValues(options);
+}
+
+Glyph.prototype.bindConstructorValues = function(options) {
+    this.index = options.index || 0;
+
+    // These three values cannnot be deferred for memory optimization:
+    this.name = options.name || null;
+    this.unicode = options.unicode || undefined;
+    this.unicodes = options.unicodes || options.unicode !== undefined ? [options.unicode] : [];
+
+    // But by binding these values only when necessary, we reduce can
+    // the memory requirements by almost 3% for larger fonts.
+    if (options.xMin) {
+        this.xMin = options.xMin;
+    }
+
+    if (options.yMin) {
+        this.yMin = options.yMin;
+    }
+
+    if (options.xMax) {
+        this.xMax = options.xMax;
+    }
+
+    if (options.yMax) {
+        this.yMax = options.yMax;
+    }
+
+    if (options.advanceWidth) {
+        this.advanceWidth = options.advanceWidth;
+    }
+
+    // The path for a glyph is the most memory intensive, and is bound as a value
+    // with a getter/setter to ensure we actually do path parsing only once the
+    // path is actually needed by anything.
+    Object.defineProperty(this, 'path', getPathDefinition(this, options.path));
+};
+
+Glyph.prototype.addUnicode = function(unicode) {
+    if (this.unicodes.length === 0) {
+        this.unicode = unicode;
+    }
+
+    this.unicodes.push(unicode);
+};
+
+// Convert the glyph to a Path we can draw on a drawing context.
+//
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.getPath = function(x, y, fontSize) {
+    x = x !== undefined ? x : 0;
+    y = y !== undefined ? y : 0;
+    fontSize = fontSize !== undefined ? fontSize : 72;
+    var scale = 1 / this.path.unitsPerEm * fontSize;
+    var p = new path.Path();
+    var commands = this.path.commands;
+    for (var i = 0; i < commands.length; i += 1) {
+        var cmd = commands[i];
+        if (cmd.type === 'M') {
+            p.moveTo(x + (cmd.x * scale), y + (-cmd.y * scale));
+        } else if (cmd.type === 'L') {
+            p.lineTo(x + (cmd.x * scale), y + (-cmd.y * scale));
+        } else if (cmd.type === 'Q') {
+            p.quadraticCurveTo(x + (cmd.x1 * scale), y + (-cmd.y1 * scale),
+                               x + (cmd.x * scale), y + (-cmd.y * scale));
+        } else if (cmd.type === 'C') {
+            p.curveTo(x + (cmd.x1 * scale), y + (-cmd.y1 * scale),
+                      x + (cmd.x2 * scale), y + (-cmd.y2 * scale),
+                      x + (cmd.x * scale), y + (-cmd.y * scale));
+        } else if (cmd.type === 'Z') {
+            p.closePath();
+        }
+    }
+
+    return p;
+};
+
+// Split the glyph into contours.
+// This function is here for backwards compatibility, and to
+// provide raw access to the TrueType glyph outlines.
+Glyph.prototype.getContours = function() {
+    if (this.points === undefined) {
+        return [];
+    }
+
+    var contours = [];
+    var currentContour = [];
+    for (var i = 0; i < this.points.length; i += 1) {
+        var pt = this.points[i];
+        currentContour.push(pt);
+        if (pt.lastPointOfContour) {
+            contours.push(currentContour);
+            currentContour = [];
+        }
+    }
+
+    check.argument(currentContour.length === 0, 'There are still points left in the current contour.');
+    return contours;
+};
+
+// Calculate the xMin/yMin/xMax/yMax/lsb/rsb for a Glyph.
+Glyph.prototype.getMetrics = function() {
+    var commands = this.path.commands;
+    var xCoords = [];
+    var yCoords = [];
+    for (var i = 0; i < commands.length; i += 1) {
+        var cmd = commands[i];
+        if (cmd.type !== 'Z') {
+            xCoords.push(cmd.x);
+            yCoords.push(cmd.y);
+        }
+
+        if (cmd.type === 'Q' || cmd.type === 'C') {
+            xCoords.push(cmd.x1);
+            yCoords.push(cmd.y1);
+        }
+
+        if (cmd.type === 'C') {
+            xCoords.push(cmd.x2);
+            yCoords.push(cmd.y2);
+        }
+    }
+
+    var metrics = {
+        xMin: Math.min.apply(null, xCoords),
+        yMin: Math.min.apply(null, yCoords),
+        xMax: Math.max.apply(null, xCoords),
+        yMax: Math.max.apply(null, yCoords),
+        leftSideBearing: 0
+    };
+    metrics.rightSideBearing = this.advanceWidth - metrics.leftSideBearing - (metrics.xMax - metrics.xMin);
+    return metrics;
+};
+
+// Draw the glyph on the given context.
+//
+// ctx - The drawing context.
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.draw = function(ctx, x, y, fontSize) {
+    this.getPath(x, y, fontSize).draw(ctx);
+};
+
+// Draw the points of the glyph.
+// On-curve points will be drawn in blue, off-curve points will be drawn in red.
+//
+// ctx - The drawing context.
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.drawPoints = function(ctx, x, y, fontSize) {
+
+    function drawCircles(l, x, y, scale) {
+        var PI_SQ = Math.PI * 2;
+        ctx.beginPath();
+        for (var j = 0; j < l.length; j += 1) {
+            ctx.moveTo(x + (l[j].x * scale), y + (l[j].y * scale));
+            ctx.arc(x + (l[j].x * scale), y + (l[j].y * scale), 2, 0, PI_SQ, false);
+        }
+
+        ctx.closePath();
+        ctx.fill();
+    }
+
+    x = x !== undefined ? x : 0;
+    y = y !== undefined ? y : 0;
+    fontSize = fontSize !== undefined ? fontSize : 24;
+    var scale = 1 / this.path.unitsPerEm * fontSize;
+
+    var blueCircles = [];
+    var redCircles = [];
+    var path = this.path;
+    for (var i = 0; i < path.commands.length; i += 1) {
+        var cmd = path.commands[i];
+        if (cmd.x !== undefined) {
+            blueCircles.push({x: cmd.x, y: -cmd.y});
+        }
+
+        if (cmd.x1 !== undefined) {
+            redCircles.push({x: cmd.x1, y: -cmd.y1});
+        }
+
+        if (cmd.x2 !== undefined) {
+            redCircles.push({x: cmd.x2, y: -cmd.y2});
+        }
+    }
+
+    ctx.fillStyle = 'blue';
+    drawCircles(blueCircles, x, y, scale);
+    ctx.fillStyle = 'red';
+    drawCircles(redCircles, x, y, scale);
+};
+
+// Draw lines indicating important font measurements.
+// Black lines indicate the origin of the coordinate system (point 0,0).
+// Blue lines indicate the glyph bounding box.
+// Green line indicates the advance width of the glyph.
+//
+// ctx - The drawing context.
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.drawMetrics = function(ctx, x, y, fontSize) {
+    var scale;
+    x = x !== undefined ? x : 0;
+    y = y !== undefined ? y : 0;
+    fontSize = fontSize !== undefined ? fontSize : 24;
+    scale = 1 / this.path.unitsPerEm * fontSize;
+    ctx.lineWidth = 1;
+
+    // Draw the origin
+    ctx.strokeStyle = 'black';
+    draw.line(ctx, x, -10000, x, 10000);
+    draw.line(ctx, -10000, y, 10000, y);
+
+    // This code is here due to memory optimization: by not using
+    // defaults in the constructor, we save a notable amount of memory.
+    var xMin = this.xMin || 0;
+    var yMin = this.yMin || 0;
+    var xMax = this.xMax || 0;
+    var yMax = this.yMax || 0;
+    var advanceWidth = this.advanceWidth || 0;
+
+    // Draw the glyph box
+    ctx.strokeStyle = 'blue';
+    draw.line(ctx, x + (xMin * scale), -10000, x + (xMin * scale), 10000);
+    draw.line(ctx, x + (xMax * scale), -10000, x + (xMax * scale), 10000);
+    draw.line(ctx, -10000, y + (-yMin * scale), 10000, y + (-yMin * scale));
+    draw.line(ctx, -10000, y + (-yMax * scale), 10000, y + (-yMax * scale));
+
+    // Draw the advance width
+    ctx.strokeStyle = 'green';
+    draw.line(ctx, x + (advanceWidth * scale), -10000, x + (advanceWidth * scale), 10000);
+};
+
+exports.Glyph = Glyph;
+
+},{"./check":2,"./draw":3,"./path":10}],7:[function(_dereq_,module,exports){
+// The GlyphSet object
+
+'use strict';
+
+var _glyph = _dereq_('./glyph');
+
+// A GlyphSet represents all glyphs available in the font, but modelled using
+// a deferred glyph loader, for retrieving glyphs only once they are absolutely
+// necessary, to keep the memory footprint down.
+function GlyphSet(font, glyphs) {
+    this.font = font;
+    this.glyphs = {};
+    if (Array.isArray(glyphs)) {
+        for (var i = 0; i < glyphs.length; i++) {
+            this.glyphs[i] = glyphs[i];
+        }
+    }
+
+    this.length = (glyphs && glyphs.length) || 0;
+}
+
+GlyphSet.prototype.get = function(index) {
+    if (typeof this.glyphs[index] === 'function') {
+        this.glyphs[index] = this.glyphs[index]();
+    }
+
+    return this.glyphs[index];
+};
+
+GlyphSet.prototype.push = function(index, loader) {
+    this.glyphs[index] = loader;
+    this.length++;
+};
+
+function glyphLoader(font, index) {
+    return new _glyph.Glyph({index: index, font: font});
+}
+
+/**
+ * Generate a stub glyph that can be filled with all metadata *except*
+ * the "points" and "path" properties, which must be loaded only once
+ * the glyph's path is actually requested for text shaping.
+ */
+
+function ttfGlyphLoader(font, index, parseGlyph, data, position, buildPath) {
+    return function() {
+        var glyph = new _glyph.Glyph({index: index, font: font});
+
+        glyph.path = function() {
+            parseGlyph(glyph, data, position);
+            var path = buildPath(font.glyphs, glyph);
+            path.unitsPerEm = font.unitsPerEm;
+            return path;
+        };
+
+        return glyph;
+    };
+}
+
+function cffGlyphLoader(font, index, parseCFFCharstring, charstring) {
+    return function() {
+        var glyph = new _glyph.Glyph({index: index, font: font});
+
+        glyph.path = function() {
+            var path = parseCFFCharstring(font, glyph, charstring);
+            path.unitsPerEm = font.unitsPerEm;
+            return path;
+        };
+
+        return glyph;
+    };
+}
+
+exports.GlyphSet = GlyphSet;
+exports.glyphLoader = glyphLoader;
+exports.ttfGlyphLoader = ttfGlyphLoader;
+exports.cffGlyphLoader = cffGlyphLoader;
+
+},{"./glyph":6}],8:[function(_dereq_,module,exports){
+// opentype.js
+// https://github.com/nodebox/opentype.js
+// (c) 2015 Frederik De Bleser
+// opentype.js may be freely distributed under the MIT license.
+
+/* global ArrayBuffer, DataView, Uint8Array, XMLHttpRequest  */
+
+'use strict';
+
+var encoding = _dereq_('./encoding');
+var _font = _dereq_('./font');
+var glyph = _dereq_('./glyph');
+var parse = _dereq_('./parse');
+var path = _dereq_('./path');
+
+var cmap = _dereq_('./tables/cmap');
+var cff = _dereq_('./tables/cff');
+var glyf = _dereq_('./tables/glyf');
+var gpos = _dereq_('./tables/gpos');
+var head = _dereq_('./tables/head');
+var hhea = _dereq_('./tables/hhea');
+var hmtx = _dereq_('./tables/hmtx');
+var kern = _dereq_('./tables/kern');
+var loca = _dereq_('./tables/loca');
+var maxp = _dereq_('./tables/maxp');
+var _name = _dereq_('./tables/name');
+var os2 = _dereq_('./tables/os2');
+var post = _dereq_('./tables/post');
+
+// File loaders /////////////////////////////////////////////////////////
+
+// Convert a Node.js Buffer to an ArrayBuffer
+function toArrayBuffer(buffer) {
+    var arrayBuffer = new ArrayBuffer(buffer.length);
+    var data = new Uint8Array(arrayBuffer);
+    for (var i = 0; i < buffer.length; i += 1) {
+        data[i] = buffer[i];
+    }
+
+    return arrayBuffer;
+}
+
+function loadFromFile(path, callback) {
+    var fs = _dereq_('fs');
+    fs.readFile(path, function(err, buffer) {
+        if (err) {
+            return callback(err.message);
+        }
+
+        callback(null, toArrayBuffer(buffer));
+    });
+}
+
+function loadFromUrl(url, callback) {
+    var request = new XMLHttpRequest();
+    request.open('get', url, true);
+    request.responseType = 'arraybuffer';
+    request.onload = function() {
+        if (request.status !== 200) {
+            return callback('Font could not be loaded: ' + request.statusText);
+        }
+
+        return callback(null, request.response);
+    };
+
+    request.send();
+}
+
+// Public API ///////////////////////////////////////////////////////////
+
+// Parse the OpenType file data (as an ArrayBuffer) and return a Font object.
+// If the file could not be parsed (most likely because it contains Postscript outlines)
+// we return an empty Font object with the `supported` flag set to `false`.
+function parseBuffer(buffer) {
+    var indexToLocFormat;
+    var hmtxOffset;
+    var glyfOffset;
+    var locaOffset;
+    var cffOffset;
+    var kernOffset;
+    var gposOffset;
+
+    // OpenType fonts use big endian byte ordering.
+    // We can't rely on typed array view types, because they operate with the endianness of the host computer.
+    // Instead we use DataViews where we can specify endianness.
+
+    var font = new _font.Font();
+    var data = new DataView(buffer, 0);
+
+    var version = parse.getFixed(data, 0);
+    if (version === 1.0) {
+        font.outlinesFormat = 'truetype';
+    } else {
+        version = parse.getTag(data, 0);
+        if (version === 'OTTO') {
+            font.outlinesFormat = 'cff';
+        } else {
+            throw new Error('Unsupported OpenType version ' + version);
+        }
+    }
+
+    var numTables = parse.getUShort(data, 4);
+
+    // Offset into the table records.
+    var p = 12;
+    for (var i = 0; i < numTables; i += 1) {
+        var tag = parse.getTag(data, p);
+        var offset = parse.getULong(data, p + 8);
+        switch (tag) {
+        case 'cmap':
+            font.tables.cmap = cmap.parse(data, offset);
+            font.encoding = new encoding.CmapEncoding(font.tables.cmap);
+            if (!font.encoding) {
+                font.supported = false;
+            }
+
+            break;
+        case 'head':
+            font.tables.head = head.parse(data, offset);
+            font.unitsPerEm = font.tables.head.unitsPerEm;
+            indexToLocFormat = font.tables.head.indexToLocFormat;
+            break;
+        case 'hhea':
+            font.tables.hhea = hhea.parse(data, offset);
+            font.ascender = font.tables.hhea.ascender;
+            font.descender = font.tables.hhea.descender;
+            font.numberOfHMetrics = font.tables.hhea.numberOfHMetrics;
+            break;
+        case 'hmtx':
+            hmtxOffset = offset;
+            break;
+        case 'maxp':
+            font.tables.maxp = maxp.parse(data, offset);
+            font.numGlyphs = font.tables.maxp.numGlyphs;
+            break;
+        case 'name':
+            font.tables.name = _name.parse(data, offset);
+            font.familyName = font.tables.name.fontFamily;
+            font.styleName = font.tables.name.fontSubfamily;
+            break;
+        case 'OS/2':
+            font.tables.os2 = os2.parse(data, offset);
+            break;
+        case 'post':
+            font.tables.post = post.parse(data, offset);
+            font.glyphNames = new encoding.GlyphNames(font.tables.post);
+            break;
+        case 'glyf':
+            glyfOffset = offset;
+            break;
+        case 'loca':
+            locaOffset = offset;
+            break;
+        case 'CFF ':
+            cffOffset = offset;
+            break;
+        case 'kern':
+            kernOffset = offset;
+            break;
+        case 'GPOS':
+            gposOffset = offset;
+            break;
+        }
+        p += 16;
+    }
+
+    if (glyfOffset && locaOffset) {
+        var shortVersion = indexToLocFormat === 0;
+        var locaTable = loca.parse(data, locaOffset, font.numGlyphs, shortVersion);
+        font.glyphs = glyf.parse(data, glyfOffset, locaTable, font);
+        hmtx.parse(data, hmtxOffset, font.numberOfHMetrics, font.numGlyphs, font.glyphs);
+        encoding.addGlyphNames(font);
+    } else if (cffOffset) {
+        cff.parse(data, cffOffset, font);
+        encoding.addGlyphNames(font);
+    } else {
+        font.supported = false;
+    }
+
+    if (font.supported) {
+        if (kernOffset) {
+            font.kerningPairs = kern.parse(data, kernOffset);
+        } else {
+            font.kerningPairs = {};
+        }
+
+        if (gposOffset) {
+            gpos.parse(data, gposOffset, font);
+        }
+    }
+
+    return font;
+}
+
+// Asynchronously load the font from a URL or a filesystem. When done, call the callback
+// with two arguments `(err, font)`. The `err` will be null on success,
+// the `font` is a Font object.
+//
+// We use the node.js callback convention so that
+// opentype.js can integrate with frameworks like async.js.
+function load(url, callback) {
+    var isNode = typeof window === 'undefined';
+    var loadFn = isNode ? loadFromFile : loadFromUrl;
+    loadFn(url, function(err, arrayBuffer) {
+        if (err) {
+            return callback(err);
+        }
+
+        var font = parseBuffer(arrayBuffer);
+        if (!font.supported) {
+            return callback('Font is not supported (is this a Postscript font?)');
+        }
+
+        return callback(null, font);
+    });
+}
+
+exports._parse = parse;
+exports.Font = _font.Font;
+exports.Glyph = glyph.Glyph;
+exports.Path = path.Path;
+exports.parse = parseBuffer;
+exports.load = load;
+
+},{"./encoding":4,"./font":5,"./glyph":6,"./parse":9,"./path":10,"./tables/cff":12,"./tables/cmap":13,"./tables/glyf":14,"./tables/gpos":15,"./tables/head":16,"./tables/hhea":17,"./tables/hmtx":18,"./tables/kern":19,"./tables/loca":20,"./tables/maxp":21,"./tables/name":22,"./tables/os2":23,"./tables/post":24,"fs":1}],9:[function(_dereq_,module,exports){
+// Parsing utility functions
+
+'use strict';
+
+// Retrieve an unsigned byte from the DataView.
+exports.getByte = function getByte(dataView, offset) {
+    return dataView.getUint8(offset);
+};
+
+exports.getCard8 = exports.getByte;
+
+// Retrieve an unsigned 16-bit short from the DataView.
+// The value is stored in big endian.
+exports.getUShort = function(dataView, offset) {
+    return dataView.getUint16(offset, false);
+};
+
+exports.getCard16 = exports.getUShort;
+
+// Retrieve a signed 16-bit short from the DataView.
+// The value is stored in big endian.
+exports.getShort = function(dataView, offset) {
+    return dataView.getInt16(offset, false);
+};
+
+// Retrieve an unsigned 32-bit long from the DataView.
+// The value is stored in big endian.
+exports.getULong = function(dataView, offset) {
+    return dataView.getUint32(offset, false);
+};
+
+// Retrieve a 32-bit signed fixed-point number (16.16) from the DataView.
+// The value is stored in big endian.
+exports.getFixed = function(dataView, offset) {
+    var decimal = dataView.getInt16(offset, false);
+    var fraction = dataView.getUint16(offset + 2, false);
+    return decimal + fraction / 65535;
+};
+
+// Retrieve a 4-character tag from the DataView.
+// Tags are used to identify tables.
+exports.getTag = function(dataView, offset) {
+    var tag = '';
+    for (var i = offset; i < offset + 4; i += 1) {
+        tag += String.fromCharCode(dataView.getInt8(i));
+    }
+
+    return tag;
+};
+
+// Retrieve an offset from the DataView.
+// Offsets are 1 to 4 bytes in length, depending on the offSize argument.
+exports.getOffset = function(dataView, offset, offSize) {
+    var v = 0;
+    for (var i = 0; i < offSize; i += 1) {
+        v <<= 8;
+        v += dataView.getUint8(offset + i);
+    }
+
+    return v;
+};
+
+// Retrieve a number of bytes from start offset to the end offset from the DataView.
+exports.getBytes = function(dataView, startOffset, endOffset) {
+    var bytes = [];
+    for (var i = startOffset; i < endOffset; i += 1) {
+        bytes.push(dataView.getUint8(i));
+    }
+
+    return bytes;
+};
+
+// Convert the list of bytes to a string.
+exports.bytesToString = function(bytes) {
+    var s = '';
+    for (var i = 0; i < bytes.length; i += 1) {
+        s += String.fromCharCode(bytes[i]);
+    }
+
+    return s;
+};
+
+var typeOffsets = {
+    byte: 1,
+    uShort: 2,
+    short: 2,
+    uLong: 4,
+    fixed: 4,
+    longDateTime: 8,
+    tag: 4
+};
+
+// A stateful parser that changes the offset whenever a value is retrieved.
+// The data is a DataView.
+function Parser(data, offset) {
+    this.data = data;
+    this.offset = offset;
+    this.relativeOffset = 0;
+}
+
+Parser.prototype.parseByte = function() {
+    var v = this.data.getUint8(this.offset + this.relativeOffset);
+    this.relativeOffset += 1;
+    return v;
+};
+
+Parser.prototype.parseChar = function() {
+    var v = this.data.getInt8(this.offset + this.relativeOffset);
+    this.relativeOffset += 1;
+    return v;
+};
+
+Parser.prototype.parseCard8 = Parser.prototype.parseByte;
+
+Parser.prototype.parseUShort = function() {
+    var v = this.data.getUint16(this.offset + this.relativeOffset);
+    this.relativeOffset += 2;
+    return v;
+};
+
+Parser.prototype.parseCard16 = Parser.prototype.parseUShort;
+Parser.prototype.parseSID = Parser.prototype.parseUShort;
+Parser.prototype.parseOffset16 = Parser.prototype.parseUShort;
+
+Parser.prototype.parseShort = function() {
+    var v = this.data.getInt16(this.offset + this.relativeOffset);
+    this.relativeOffset += 2;
+    return v;
+};
+
+Parser.prototype.parseF2Dot14 = function() {
+    var v = this.data.getInt16(this.offset + this.relativeOffset) / 16384;
+    this.relativeOffset += 2;
+    return v;
+};
+
+Parser.prototype.parseULong = function() {
+    var v = exports.getULong(this.data, this.offset + this.relativeOffset);
+    this.relativeOffset += 4;
+    return v;
+};
+
+Parser.prototype.parseFixed = function() {
+    var v = exports.getFixed(this.data, this.offset + this.relativeOffset);
+    this.relativeOffset += 4;
+    return v;
+};
+
+Parser.prototype.parseOffset16List =
+Parser.prototype.parseUShortList = function(count) {
+    var offsets = new Array(count);
+    var dataView = this.data;
+    var offset = this.offset + this.relativeOffset;
+    for (var i = 0; i < count; i++) {
+        offsets[i] = exports.getUShort(dataView, offset);
+        offset += 2;
+    }
+
+    this.relativeOffset += count * 2;
+    return offsets;
+};
+
+Parser.prototype.parseString = function(length) {
+    var dataView = this.data;
+    var offset = this.offset + this.relativeOffset;
+    var string = '';
+    this.relativeOffset += length;
+    for (var i = 0; i < length; i++) {
+        string += String.fromCharCode(dataView.getUint8(offset + i));
+    }
+
+    return string;
+};
+
+Parser.prototype.parseTag = function() {
+    return this.parseString(4);
+};
+
+// LONGDATETIME is a 64-bit integer.
+// JavaScript and unix timestamps traditionally use 32 bits, so we
+// only take the last 32 bits.
+Parser.prototype.parseLongDateTime = function() {
+    var v = exports.getULong(this.data, this.offset + this.relativeOffset + 4);
+    this.relativeOffset += 8;
+    return v;
+};
+
+Parser.prototype.parseFixed = function() {
+    var v = exports.getULong(this.data, this.offset + this.relativeOffset);
+    this.relativeOffset += 4;
+    return v / 65536;
+};
+
+Parser.prototype.parseVersion = function() {
+    var major = exports.getUShort(this.data, this.offset + this.relativeOffset);
+
+    // How to interpret the minor version is very vague in the spec. 0x5000 is 5, 0x1000 is 1
+    // This returns the correct number if minor = 0xN000 where N is 0-9
+    var minor = exports.getUShort(this.data, this.offset + this.relativeOffset + 2);
+    this.relativeOffset += 4;
+    return major + minor / 0x1000 / 10;
+};
+
+Parser.prototype.skip = function(type, amount) {
+    if (amount === undefined) {
+        amount = 1;
+    }
+
+    this.relativeOffset += typeOffsets[type] * amount;
+};
+
+exports.Parser = Parser;
+
+},{}],10:[function(_dereq_,module,exports){
+// Geometric objects
+
+'use strict';
+
+// A bézier path containing a set of path commands similar to a SVG path.
+// Paths can be drawn on a context using `draw`.
+function Path() {
+    this.commands = [];
+    this.fill = 'black';
+    this.stroke = null;
+    this.strokeWidth = 1;
+}
+
+Path.prototype.moveTo = function(x, y) {
+    this.commands.push({
+        type: 'M',
+        x: x,
+        y: y
+    });
+};
+
+Path.prototype.lineTo = function(x, y) {
+    this.commands.push({
+        type: 'L',
+        x: x,
+        y: y
+    });
+};
+
+Path.prototype.curveTo = Path.prototype.bezierCurveTo = function(x1, y1, x2, y2, x, y) {
+    this.commands.push({
+        type: 'C',
+        x1: x1,
+        y1: y1,
+        x2: x2,
+        y2: y2,
+        x: x,
+        y: y
+    });
+};
+
+Path.prototype.quadTo = Path.prototype.quadraticCurveTo = function(x1, y1, x, y) {
+    this.commands.push({
+        type: 'Q',
+        x1: x1,
+        y1: y1,
+        x: x,
+        y: y
+    });
+};
+
+Path.prototype.close = Path.prototype.closePath = function() {
+    this.commands.push({
+        type: 'Z'
+    });
+};
+
+// Add the given path or list of commands to the commands of this path.
+Path.prototype.extend = function(pathOrCommands) {
+    if (pathOrCommands.commands) {
+        pathOrCommands = pathOrCommands.commands;
+    }
+
+    Array.prototype.push.apply(this.commands, pathOrCommands);
+};
+
+// Draw the path to a 2D context.
+Path.prototype.draw = function(ctx) {
+    ctx.beginPath();
+    for (var i = 0; i < this.commands.length; i += 1) {
+        var cmd = this.commands[i];
+        if (cmd.type === 'M') {
+            ctx.moveTo(cmd.x, cmd.y);
+        } else if (cmd.type === 'L') {
+            ctx.lineTo(cmd.x, cmd.y);
+        } else if (cmd.type === 'C') {
+            ctx.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+        } else if (cmd.type === 'Q') {
+            ctx.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y);
+        } else if (cmd.type === 'Z') {
+            ctx.closePath();
+        }
+    }
+
+    if (this.fill) {
+        ctx.fillStyle = this.fill;
+        ctx.fill();
+    }
+
+    if (this.stroke) {
+        ctx.strokeStyle = this.stroke;
+        ctx.lineWidth = this.strokeWidth;
+        ctx.stroke();
+    }
+};
+
+// Convert the Path to a string of path data instructions
+// See http://www.w3.org/TR/SVG/paths.html#PathData
+// Parameters:
+// - decimalPlaces: The amount of decimal places for floating-point values (default: 2)
+Path.prototype.toPathData = function(decimalPlaces) {
+    decimalPlaces = decimalPlaces !== undefined ? decimalPlaces : 2;
+
+    function floatToString(v) {
+        if (Math.round(v) === v) {
+            return '' + Math.round(v);
+        } else {
+            return v.toFixed(decimalPlaces);
+        }
+    }
+
+    function packValues() {
+        var s = '';
+        for (var i = 0; i < arguments.length; i += 1) {
+            var v = arguments[i];
+            if (v >= 0 && i > 0) {
+                s += ' ';
+            }
+
+            s += floatToString(v);
+        }
+
+        return s;
+    }
+
+    var d = '';
+    for (var i = 0; i < this.commands.length; i += 1) {
+        var cmd = this.commands[i];
+        if (cmd.type === 'M') {
+            d += 'M' + packValues(cmd.x, cmd.y);
+        } else if (cmd.type === 'L') {
+            d += 'L' + packValues(cmd.x, cmd.y);
+        } else if (cmd.type === 'C') {
+            d += 'C' + packValues(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+        } else if (cmd.type === 'Q') {
+            d += 'Q' + packValues(cmd.x1, cmd.y1, cmd.x, cmd.y);
+        } else if (cmd.type === 'Z') {
+            d += 'Z';
+        }
+    }
+
+    return d;
+};
+
+// Convert the path to a SVG <path> element, as a string.
+// Parameters:
+// - decimalPlaces: The amount of decimal places for floating-point values (default: 2)
+Path.prototype.toSVG = function(decimalPlaces) {
+    var svg = '<path d="';
+    svg += this.toPathData(decimalPlaces);
+    svg += '"';
+    if (this.fill & this.fill !== 'black') {
+        if (this.fill === null) {
+            svg += ' fill="none"';
+        } else {
+            svg += ' fill="' + this.fill + '"';
+        }
+    }
+
+    if (this.stroke) {
+        svg += ' stroke="' + this.stroke + '" stroke-width="' + this.strokeWidth + '"';
+    }
+
+    svg += '/>';
+    return svg;
+};
+
+exports.Path = Path;
+
+},{}],11:[function(_dereq_,module,exports){
+// Table metadata
+
+'use strict';
+
+var check = _dereq_('./check');
+var encode = _dereq_('./types').encode;
+var sizeOf = _dereq_('./types').sizeOf;
+
+function Table(tableName, fields, options) {
+    var i;
+    for (i = 0; i < fields.length; i += 1) {
+        var field = fields[i];
+        this[field.name] = field.value;
+    }
+
+    this.tableName = tableName;
+    this.fields = fields;
+    if (options) {
+        var optionKeys = Object.keys(options);
+        for (i = 0; i < optionKeys.length; i += 1) {
+            var k = optionKeys[i];
+            var v = options[k];
+            if (this[k] !== undefined) {
+                this[k] = v;
+            }
+        }
+    }
+}
+
+Table.prototype.sizeOf = function() {
+    var v = 0;
+    for (var i = 0; i < this.fields.length; i += 1) {
+        var field = this.fields[i];
+        var value = this[field.name];
+        if (value === undefined) {
+            value = field.value;
+        }
+
+        if (typeof value.sizeOf === 'function') {
+            v += value.sizeOf();
+        } else {
+            var sizeOfFunction = sizeOf[field.type];
+            check.assert(typeof sizeOfFunction === 'function', 'Could not find sizeOf function for field' + field.name);
+            v += sizeOfFunction(value);
+        }
+    }
+
+    return v;
+};
+
+Table.prototype.encode = function() {
+    return encode.TABLE(this);
+};
+
+exports.Table = Table;
+
+},{"./check":2,"./types":26}],12:[function(_dereq_,module,exports){
+// The `CFF` table contains the glyph outlines in PostScript format.
+// https://www.microsoft.com/typography/OTSPEC/cff.htm
+// http://download.microsoft.com/download/8/0/1/801a191c-029d-4af3-9642-555f6fe514ee/cff.pdf
+// http://download.microsoft.com/download/8/0/1/801a191c-029d-4af3-9642-555f6fe514ee/type2.pdf
+
+'use strict';
+
+var encoding = _dereq_('../encoding');
+var glyphset = _dereq_('../glyphset');
+var parse = _dereq_('../parse');
+var path = _dereq_('../path');
+var table = _dereq_('../table');
+
+// Custom equals function that can also check lists.
+function equals(a, b) {
+    if (a === b) {
+        return true;
+    } else if (Array.isArray(a) && Array.isArray(b)) {
+        if (a.length !== b.length) {
+            return false;
+        }
+
+        for (var i = 0; i < a.length; i += 1) {
+            if (!equals(a[i], b[i])) {
+                return false;
+            }
+        }
+
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// Parse a `CFF` INDEX array.
+// An index array consists of a list of offsets, then a list of objects at those offsets.
+function parseCFFIndex(data, start, conversionFn) {
+    //var i, objectOffset, endOffset;
+    var offsets = [];
+    var objects = [];
+    var count = parse.getCard16(data, start);
+    var i;
+    var objectOffset;
+    var endOffset;
+    if (count !== 0) {
+        var offsetSize = parse.getByte(data, start + 2);
+        objectOffset = start + ((count + 1) * offsetSize) + 2;
+        var pos = start + 3;
+        for (i = 0; i < count + 1; i += 1) {
+            offsets.push(parse.getOffset(data, pos, offsetSize));
+            pos += offsetSize;
+        }
+
+        // The total size of the index array is 4 header bytes + the value of the last offset.
+        endOffset = objectOffset + offsets[count];
+    } else {
+        endOffset = start + 2;
+    }
+
+    for (i = 0; i < offsets.length - 1; i += 1) {
+        var value = parse.getBytes(data, objectOffset + offsets[i], objectOffset + offsets[i + 1]);
+        if (conversionFn) {
+            value = conversionFn(value);
+        }
+
+        objects.push(value);
+    }
+
+    return {objects: objects, startOffset: start, endOffset: endOffset};
+}
+
+// Parse a `CFF` DICT real value.
+function parseFloatOperand(parser) {
+    var s = '';
+    var eof = 15;
+    var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', null, '-'];
+    while (true) {
+        var b = parser.parseByte();
+        var n1 = b >> 4;
+        var n2 = b & 15;
+
+        if (n1 === eof) {
+            break;
+        }
+
+        s += lookup[n1];
+
+        if (n2 === eof) {
+            break;
+        }
+
+        s += lookup[n2];
+    }
+
+    return parseFloat(s);
+}
+
+// Parse a `CFF` DICT operand.
+function parseOperand(parser, b0) {
+    var b1;
+    var b2;
+    var b3;
+    var b4;
+    if (b0 === 28) {
+        b1 = parser.parseByte();
+        b2 = parser.parseByte();
+        return b1 << 8 | b2;
+    }
+
+    if (b0 === 29) {
+        b1 = parser.parseByte();
+        b2 = parser.parseByte();
+        b3 = parser.parseByte();
+        b4 = parser.parseByte();
+        return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+    }
+
+    if (b0 === 30) {
+        return parseFloatOperand(parser);
+    }
+
+    if (b0 >= 32 && b0 <= 246) {
+        return b0 - 139;
+    }
+
+    if (b0 >= 247 && b0 <= 250) {
+        b1 = parser.parseByte();
+        return (b0 - 247) * 256 + b1 + 108;
+    }
+
+    if (b0 >= 251 && b0 <= 254) {
+        b1 = parser.parseByte();
+        return -(b0 - 251) * 256 - b1 - 108;
+    }
+
+    throw new Error('Invalid b0 ' + b0);
+}
+
+// Convert the entries returned by `parseDict` to a proper dictionary.
+// If a value is a list of one, it is unpacked.
+function entriesToObject(entries) {
+    var o = {};
+    for (var i = 0; i < entries.length; i += 1) {
+        var key = entries[i][0];
+        var values = entries[i][1];
+        var value;
+        if (values.length === 1) {
+            value = values[0];
+        } else {
+            value = values;
+        }
+
+        if (o.hasOwnProperty(key)) {
+            throw new Error('Object ' + o + ' already has key ' + key);
+        }
+
+        o[key] = value;
+    }
+
+    return o;
+}
+
+// Parse a `CFF` DICT object.
+// A dictionary contains key-value pairs in a compact tokenized format.
+function parseCFFDict(data, start, size) {
+    start = start !== undefined ? start : 0;
+    var parser = new parse.Parser(data, start);
+    var entries = [];
+    var operands = [];
+    size = size !== undefined ? size : data.length;
+
+    while (parser.relativeOffset < size) {
+        var op = parser.parseByte();
+
+        // The first byte for each dict item distinguishes between operator (key) and operand (value).
+        // Values <= 21 are operators.
+        if (op <= 21) {
+            // Two-byte operators have an initial escape byte of 12.
+            if (op === 12) {
+                op = 1200 + parser.parseByte();
+            }
+
+            entries.push([op, operands]);
+            operands = [];
+        } else {
+            // Since the operands (values) come before the operators (keys), we store all operands in a list
+            // until we encounter an operator.
+            operands.push(parseOperand(parser, op));
+        }
+    }
+
+    return entriesToObject(entries);
+}
+
+// Given a String Index (SID), return the value of the string.
+// Strings below index 392 are standard CFF strings and are not encoded in the font.
+function getCFFString(strings, index) {
+    if (index <= 390) {
+        index = encoding.cffStandardStrings[index];
+    } else {
+        index = strings[index - 391];
+    }
+
+    return index;
+}
+
+// Interpret a dictionary and return a new dictionary with readable keys and values for missing entries.
+// This function takes `meta` which is a list of objects containing `operand`, `name` and `default`.
+function interpretDict(dict, meta, strings) {
+    var newDict = {};
+
+    // Because we also want to include missing values, we start out from the meta list
+    // and lookup values in the dict.
+    for (var i = 0; i < meta.length; i += 1) {
+        var m = meta[i];
+        var value = dict[m.op];
+        if (value === undefined) {
+            value = m.value !== undefined ? m.value : null;
+        }
+
+        if (m.type === 'SID') {
+            value = getCFFString(strings, value);
+        }
+
+        newDict[m.name] = value;
+    }
+
+    return newDict;
+}
+
+// Parse the CFF header.
+function parseCFFHeader(data, start) {
+    var header = {};
+    header.formatMajor = parse.getCard8(data, start);
+    header.formatMinor = parse.getCard8(data, start + 1);
+    header.size = parse.getCard8(data, start + 2);
+    header.offsetSize = parse.getCard8(data, start + 3);
+    header.startOffset = start;
+    header.endOffset = start + 4;
+    return header;
+}
+
+var TOP_DICT_META = [
+    {name: 'version', op: 0, type: 'SID'},
+    {name: 'notice', op: 1, type: 'SID'},
+    {name: 'copyright', op: 1200, type: 'SID'},
+    {name: 'fullName', op: 2, type: 'SID'},
+    {name: 'familyName', op: 3, type: 'SID'},
+    {name: 'weight', op: 4, type: 'SID'},
+    {name: 'isFixedPitch', op: 1201, type: 'number', value: 0},
+    {name: 'italicAngle', op: 1202, type: 'number', value: 0},
+    {name: 'underlinePosition', op: 1203, type: 'number', value: -100},
+    {name: 'underlineThickness', op: 1204, type: 'number', value: 50},
+    {name: 'paintType', op: 1205, type: 'number', value: 0},
+    {name: 'charstringType', op: 1206, type: 'number', value: 2},
+    {name: 'fontMatrix', op: 1207, type: ['real', 'real', 'real', 'real', 'real', 'real'], value: [0.001, 0, 0, 0.001, 0, 0]},
+    {name: 'uniqueId', op: 13, type: 'number'},
+    {name: 'fontBBox', op: 5, type: ['number', 'number', 'number', 'number'], value: [0, 0, 0, 0]},
+    {name: 'strokeWidth', op: 1208, type: 'number', value: 0},
+    {name: 'xuid', op: 14, type: [], value: null},
+    {name: 'charset', op: 15, type: 'offset', value: 0},
+    {name: 'encoding', op: 16, type: 'offset', value: 0},
+    {name: 'charStrings', op: 17, type: 'offset', value: 0},
+    {name: 'private', op: 18, type: ['number', 'offset'], value: [0, 0]}
+];
+
+var PRIVATE_DICT_META = [
+    {name: 'subrs', op: 19, type: 'offset', value: 0},
+    {name: 'defaultWidthX', op: 20, type: 'number', value: 0},
+    {name: 'nominalWidthX', op: 21, type: 'number', value: 0}
+];
+
+// Parse the CFF top dictionary. A CFF table can contain multiple fonts, each with their own top dictionary.
+// The top dictionary contains the essential metadata for the font, together with the private dictionary.
+function parseCFFTopDict(data, strings) {
+    var dict = parseCFFDict(data, 0, data.byteLength);
+    return interpretDict(dict, TOP_DICT_META, strings);
+}
+
+// Parse the CFF private dictionary. We don't fully parse out all the values, only the ones we need.
+function parseCFFPrivateDict(data, start, size, strings) {
+    var dict = parseCFFDict(data, start, size);
+    return interpretDict(dict, PRIVATE_DICT_META, strings);
+}
+
+// Parse the CFF charset table, which contains internal names for all the glyphs.
+// This function will return a list of glyph names.
+// See Adobe TN #5176 chapter 13, "Charsets".
+function parseCFFCharset(data, start, nGlyphs, strings) {
+    var i;
+    var sid;
+    var count;
+    var parser = new parse.Parser(data, start);
+
+    // The .notdef glyph is not included, so subtract 1.
+    nGlyphs -= 1;
+    var charset = ['.notdef'];
+
+    var format = parser.parseCard8();
+    if (format === 0) {
+        for (i = 0; i < nGlyphs; i += 1) {
+            sid = parser.parseSID();
+            charset.push(getCFFString(strings, sid));
+        }
+    } else if (format === 1) {
+        while (charset.length <= nGlyphs) {
+            sid = parser.parseSID();
+            count = parser.parseCard8();
+            for (i = 0; i <= count; i += 1) {
+                charset.push(getCFFString(strings, sid));
+                sid += 1;
+            }
+        }
+    } else if (format === 2) {
+        while (charset.length <= nGlyphs) {
+            sid = parser.parseSID();
+            count = parser.parseCard16();
+            for (i = 0; i <= count; i += 1) {
+                charset.push(getCFFString(strings, sid));
+                sid += 1;
+            }
+        }
+    } else {
+        throw new Error('Unknown charset format ' + format);
+    }
+
+    return charset;
+}
+
+// Parse the CFF encoding data. Only one encoding can be specified per font.
+// See Adobe TN #5176 chapter 12, "Encodings".
+function parseCFFEncoding(data, start, charset) {
+    var i;
+    var code;
+    var enc = {};
+    var parser = new parse.Parser(data, start);
+    var format = parser.parseCard8();
+    if (format === 0) {
+        var nCodes = parser.parseCard8();
+        for (i = 0; i < nCodes; i += 1) {
+            code = parser.parseCard8();
+            enc[code] = i;
+        }
+    } else if (format === 1) {
+        var nRanges = parser.parseCard8();
+        code = 1;
+        for (i = 0; i < nRanges; i += 1) {
+            var first = parser.parseCard8();
+            var nLeft = parser.parseCard8();
+            for (var j = first; j <= first + nLeft; j += 1) {
+                enc[j] = code;
+                code += 1;
+            }
+        }
+    } else {
+        throw new Error('Unknown encoding format ' + format);
+    }
+
+    return new encoding.CffEncoding(enc, charset);
+}
+
+// Take in charstring code and return a Glyph object.
+// The encoding is described in the Type 2 Charstring Format
+// https://www.microsoft.com/typography/OTSPEC/charstr2.htm
+function parseCFFCharstring(font, glyph, code) {
+    var c1x;
+    var c1y;
+    var c2x;
+    var c2y;
+    var p = new path.Path();
+    var stack = [];
+    var nStems = 0;
+    var haveWidth = false;
+    var width = font.defaultWidthX;
+    var open = false;
+    var x = 0;
+    var y = 0;
+
+    function newContour(x, y) {
+        if (open) {
+            p.closePath();
+        }
+
+        p.moveTo(x, y);
+        open = true;
+    }
+
+    function parseStems() {
+        var hasWidthArg;
+
+        // The number of stem operators on the stack is always even.
+        // If the value is uneven, that means a width is specified.
+        hasWidthArg = stack.length % 2 !== 0;
+        if (hasWidthArg && !haveWidth) {
+            width = stack.shift() + font.nominalWidthX;
+        }
+
+        nStems += stack.length >> 1;
+        stack.length = 0;
+        haveWidth = true;
+    }
+
+    function parse(code) {
+        var b1;
+        var b2;
+        var b3;
+        var b4;
+        var codeIndex;
+        var subrCode;
+        var jpx;
+        var jpy;
+        var c3x;
+        var c3y;
+        var c4x;
+        var c4y;
+
+        var i = 0;
+        while (i < code.length) {
+            var v = code[i];
+            i += 1;
+            switch (v) {
+            case 1: // hstem
+                parseStems();
+                break;
+            case 3: // vstem
+                parseStems();
+                break;
+            case 4: // vmoveto
+                if (stack.length > 1 && !haveWidth) {
+                    width = stack.shift() + font.nominalWidthX;
+                    haveWidth = true;
+                }
+
+                y += stack.pop();
+                newContour(x, y);
+                break;
+            case 5: // rlineto
+                while (stack.length > 0) {
+                    x += stack.shift();
+                    y += stack.shift();
+                    p.lineTo(x, y);
+                }
+
+                break;
+            case 6: // hlineto
+                while (stack.length > 0) {
+                    x += stack.shift();
+                    p.lineTo(x, y);
+                    if (stack.length === 0) {
+                        break;
+                    }
+
+                    y += stack.shift();
+                    p.lineTo(x, y);
+                }
+
+                break;
+            case 7: // vlineto
+                while (stack.length > 0) {
+                    y += stack.shift();
+                    p.lineTo(x, y);
+                    if (stack.length === 0) {
+                        break;
+                    }
+
+                    x += stack.shift();
+                    p.lineTo(x, y);
+                }
+
+                break;
+            case 8: // rrcurveto
+                while (stack.length > 0) {
+                    c1x = x + stack.shift();
+                    c1y = y + stack.shift();
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    x = c2x + stack.shift();
+                    y = c2y + stack.shift();
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                }
+
+                break;
+            case 10: // callsubr
+                codeIndex = stack.pop() + font.subrsBias;
+                subrCode = font.subrs[codeIndex];
+                if (subrCode) {
+                    parse(subrCode);
+                }
+
+                break;
+            case 11: // return
+                return;
+            case 12: // flex operators
+                v = code[i];
+                i += 1;
+                switch (v) {
+                case 35: // flex
+                    // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 dx6 dy6 fd flex (12 35) |-
+                    c1x = x   + stack.shift();    // dx1
+                    c1y = y   + stack.shift();    // dy1
+                    c2x = c1x + stack.shift();    // dx2
+                    c2y = c1y + stack.shift();    // dy2
+                    jpx = c2x + stack.shift();    // dx3
+                    jpy = c2y + stack.shift();    // dy3
+                    c3x = jpx + stack.shift();    // dx4
+                    c3y = jpy + stack.shift();    // dy4
+                    c4x = c3x + stack.shift();    // dx5
+                    c4y = c3y + stack.shift();    // dy5
+                    x = c4x + stack.shift();      // dx6
+                    y = c4y + stack.shift();      // dy6
+                    stack.shift();                // flex depth
+                    p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+                    p.curveTo(c3x, c3y, c4x, c4y, x, y);
+                    break;
+                case 34: // hflex
+                    // |- dx1 dx2 dy2 dx3 dx4 dx5 dx6 hflex (12 34) |-
+                    c1x = x   + stack.shift();    // dx1
+                    c1y = y;                      // dy1
+                    c2x = c1x + stack.shift();    // dx2
+                    c2y = c1y + stack.shift();    // dy2
+                    jpx = c2x + stack.shift();    // dx3
+                    jpy = c2y;                    // dy3
+                    c3x = jpx + stack.shift();    // dx4
+                    c3y = c2y;                    // dy4
+                    c4x = c3x + stack.shift();    // dx5
+                    c4y = y;                      // dy5
+                    x = c4x + stack.shift();      // dx6
+                    p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+                    p.curveTo(c3x, c3y, c4x, c4y, x, y);
+                    break;
+                case 36: // hflex1
+                    // |- dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6 hflex1 (12 36) |-
+                    c1x = x   + stack.shift();    // dx1
+                    c1y = y   + stack.shift();    // dy1
+                    c2x = c1x + stack.shift();    // dx2
+                    c2y = c1y + stack.shift();    // dy2
+                    jpx = c2x + stack.shift();    // dx3
+                    jpy = c2y;                    // dy3
+                    c3x = jpx + stack.shift();    // dx4
+                    c3y = c2y;                    // dy4
+                    c4x = c3x + stack.shift();    // dx5
+                    c4y = c3y + stack.shift();    // dy5
+                    x = c4x + stack.shift();      // dx6
+                    p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+                    p.curveTo(c3x, c3y, c4x, c4y, x, y);
+                    break;
+                case 37: // flex1
+                    // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 d6 flex1 (12 37) |-
+                    c1x = x   + stack.shift();    // dx1
+                    c1y = y   + stack.shift();    // dy1
+                    c2x = c1x + stack.shift();    // dx2
+                    c2y = c1y + stack.shift();    // dy2
+                    jpx = c2x + stack.shift();    // dx3
+                    jpy = c2y + stack.shift();    // dy3
+                    c3x = jpx + stack.shift();    // dx4
+                    c3y = jpy + stack.shift();    // dy4
+                    c4x = c3x + stack.shift();    // dx5
+                    c4y = c3y + stack.shift();    // dy5
+                    if (Math.abs(c4x - x) > Math.abs(c4y - y)) {
+                        x = c4x + stack.shift();
+                    } else {
+                        y = c4y + stack.shift();
+                    }
+
+                    p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+                    p.curveTo(c3x, c3y, c4x, c4y, x, y);
+                    break;
+                default:
+                    console.log('Glyph ' + glyph.index + ': unknown operator ' + 1200 + v);
+                    stack.length = 0;
+                }
+                break;
+            case 14: // endchar
+                if (stack.length > 0 && !haveWidth) {
+                    width = stack.shift() + font.nominalWidthX;
+                    haveWidth = true;
+                }
+
+                if (open) {
+                    p.closePath();
+                    open = false;
+                }
+
+                break;
+            case 18: // hstemhm
+                parseStems();
+                break;
+            case 19: // hintmask
+            case 20: // cntrmask
+                parseStems();
+                i += (nStems + 7) >> 3;
+                break;
+            case 21: // rmoveto
+                if (stack.length > 2 && !haveWidth) {
+                    width = stack.shift() + font.nominalWidthX;
+                    haveWidth = true;
+                }
+
+                y += stack.pop();
+                x += stack.pop();
+                newContour(x, y);
+                break;
+            case 22: // hmoveto
+                if (stack.length > 1 && !haveWidth) {
+                    width = stack.shift() + font.nominalWidthX;
+                    haveWidth = true;
+                }
+
+                x += stack.pop();
+                newContour(x, y);
+                break;
+            case 23: // vstemhm
+                parseStems();
+                break;
+            case 24: // rcurveline
+                while (stack.length > 2) {
+                    c1x = x + stack.shift();
+                    c1y = y + stack.shift();
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    x = c2x + stack.shift();
+                    y = c2y + stack.shift();
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                }
+
+                x += stack.shift();
+                y += stack.shift();
+                p.lineTo(x, y);
+                break;
+            case 25: // rlinecurve
+                while (stack.length > 6) {
+                    x += stack.shift();
+                    y += stack.shift();
+                    p.lineTo(x, y);
+                }
+
+                c1x = x + stack.shift();
+                c1y = y + stack.shift();
+                c2x = c1x + stack.shift();
+                c2y = c1y + stack.shift();
+                x = c2x + stack.shift();
+                y = c2y + stack.shift();
+                p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                break;
+            case 26: // vvcurveto
+                if (stack.length % 2) {
+                    x += stack.shift();
+                }
+
+                while (stack.length > 0) {
+                    c1x = x;
+                    c1y = y + stack.shift();
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    x = c2x;
+                    y = c2y + stack.shift();
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                }
+
+                break;
+            case 27: // hhcurveto
+                if (stack.length % 2) {
+                    y += stack.shift();
+                }
+
+                while (stack.length > 0) {
+                    c1x = x + stack.shift();
+                    c1y = y;
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    x = c2x + stack.shift();
+                    y = c2y;
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                }
+
+                break;
+            case 28: // shortint
+                b1 = code[i];
+                b2 = code[i + 1];
+                stack.push(((b1 << 24) | (b2 << 16)) >> 16);
+                i += 2;
+                break;
+            case 29: // callgsubr
+                codeIndex = stack.pop() + font.gsubrsBias;
+                subrCode = font.gsubrs[codeIndex];
+                if (subrCode) {
+                    parse(subrCode);
+                }
+
+                break;
+            case 30: // vhcurveto
+                while (stack.length > 0) {
+                    c1x = x;
+                    c1y = y + stack.shift();
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    x = c2x + stack.shift();
+                    y = c2y + (stack.length === 1 ? stack.shift() : 0);
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                    if (stack.length === 0) {
+                        break;
+                    }
+
+                    c1x = x + stack.shift();
+                    c1y = y;
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    y = c2y + stack.shift();
+                    x = c2x + (stack.length === 1 ? stack.shift() : 0);
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                }
+
+                break;
+            case 31: // hvcurveto
+                while (stack.length > 0) {
+                    c1x = x + stack.shift();
+                    c1y = y;
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    y = c2y + stack.shift();
+                    x = c2x + (stack.length === 1 ? stack.shift() : 0);
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                    if (stack.length === 0) {
+                        break;
+                    }
+
+                    c1x = x;
+                    c1y = y + stack.shift();
+                    c2x = c1x + stack.shift();
+                    c2y = c1y + stack.shift();
+                    x = c2x + stack.shift();
+                    y = c2y + (stack.length === 1 ? stack.shift() : 0);
+                    p.curveTo(c1x, c1y, c2x, c2y, x, y);
+                }
+
+                break;
+            default:
+                if (v < 32) {
+                    console.log('Glyph ' + glyph.index + ': unknown operator ' + v);
+                } else if (v < 247) {
+                    stack.push(v - 139);
+                } else if (v < 251) {
+                    b1 = code[i];
+                    i += 1;
+                    stack.push((v - 247) * 256 + b1 + 108);
+                } else if (v < 255) {
+                    b1 = code[i];
+                    i += 1;
+                    stack.push(-(v - 251) * 256 - b1 - 108);
+                } else {
+                    b1 = code[i];
+                    b2 = code[i + 1];
+                    b3 = code[i + 2];
+                    b4 = code[i + 3];
+                    i += 4;
+                    stack.push(((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) / 65536);
+                }
+            }
+        }
+    }
+
+    parse(code);
+
+    glyph.advanceWidth = width;
+    return p;
+}
+
+// Subroutines are encoded using the negative half of the number space.
+// See type 2 chapter 4.7 "Subroutine operators".
+function calcCFFSubroutineBias(subrs) {
+    var bias;
+    if (subrs.length < 1240) {
+        bias = 107;
+    } else if (subrs.length < 33900) {
+        bias = 1131;
+    } else {
+        bias = 32768;
+    }
+
+    return bias;
+}
+
+// Parse the `CFF` table, which contains the glyph outlines in PostScript format.
+function parseCFFTable(data, start, font) {
+    font.tables.cff = {};
+    var header = parseCFFHeader(data, start);
+    var nameIndex = parseCFFIndex(data, header.endOffset, parse.bytesToString);
+    var topDictIndex = parseCFFIndex(data, nameIndex.endOffset);
+    var stringIndex = parseCFFIndex(data, topDictIndex.endOffset, parse.bytesToString);
+    var globalSubrIndex = parseCFFIndex(data, stringIndex.endOffset);
+    font.gsubrs = globalSubrIndex.objects;
+    font.gsubrsBias = calcCFFSubroutineBias(font.gsubrs);
+
+    var topDictData = new DataView(new Uint8Array(topDictIndex.objects[0]).buffer);
+    var topDict = parseCFFTopDict(topDictData, stringIndex.objects);
+    font.tables.cff.topDict = topDict;
+
+    var privateDictOffset = start + topDict['private'][1];
+    var privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict['private'][0], stringIndex.objects);
+    font.defaultWidthX = privateDict.defaultWidthX;
+    font.nominalWidthX = privateDict.nominalWidthX;
+
+    if (privateDict.subrs !== 0) {
+        var subrOffset = privateDictOffset + privateDict.subrs;
+        var subrIndex = parseCFFIndex(data, subrOffset);
+        font.subrs = subrIndex.objects;
+        font.subrsBias = calcCFFSubroutineBias(font.subrs);
+    } else {
+        font.subrs = [];
+        font.subrsBias = 0;
+    }
+
+    // Offsets in the top dict are relative to the beginning of the CFF data, so add the CFF start offset.
+    var charStringsIndex = parseCFFIndex(data, start + topDict.charStrings);
+    font.nGlyphs = charStringsIndex.objects.length;
+
+    var charset = parseCFFCharset(data, start + topDict.charset, font.nGlyphs, stringIndex.objects);
+    if (topDict.encoding === 0) { // Standard encoding
+        font.cffEncoding = new encoding.CffEncoding(encoding.cffStandardEncoding, charset);
+    } else if (topDict.encoding === 1) { // Expert encoding
+        font.cffEncoding = new encoding.CffEncoding(encoding.cffExpertEncoding, charset);
+    } else {
+        font.cffEncoding = parseCFFEncoding(data, start + topDict.encoding, charset);
+    }
+
+    // Prefer the CMAP encoding to the CFF encoding.
+    font.encoding = font.encoding || font.cffEncoding;
+
+    font.glyphs = new glyphset.GlyphSet(font);
+    for (var i = 0; i < font.nGlyphs; i += 1) {
+        var charString = charStringsIndex.objects[i];
+        font.glyphs.push(i, glyphset.cffGlyphLoader(font, i, parseCFFCharstring, charString));
+    }
+}
+
+// Convert a string to a String ID (SID).
+// The list of strings is modified in place.
+function encodeString(s, strings) {
+    var sid;
+
+    // Is the string in the CFF standard strings?
+    var i = encoding.cffStandardStrings.indexOf(s);
+    if (i >= 0) {
+        sid = i;
+    }
+
+    // Is the string already in the string index?
+    i = strings.indexOf(s);
+    if (i >= 0) {
+        sid = i + encoding.cffStandardStrings.length;
+    } else {
+        sid = encoding.cffStandardStrings.length + strings.length;
+        strings.push(s);
+    }
+
+    return sid;
+}
+
+function makeHeader() {
+    return new table.Table('Header', [
+        {name: 'major', type: 'Card8', value: 1},
+        {name: 'minor', type: 'Card8', value: 0},
+        {name: 'hdrSize', type: 'Card8', value: 4},
+        {name: 'major', type: 'Card8', value: 1}
+    ]);
+}
+
+function makeNameIndex(fontNames) {
+    var t = new table.Table('Name INDEX', [
+        {name: 'names', type: 'INDEX', value: []}
+    ]);
+    t.names = [];
+    for (var i = 0; i < fontNames.length; i += 1) {
+        t.names.push({name: 'name_' + i, type: 'NAME', value: fontNames[i]});
+    }
+
+    return t;
+}
+
+// Given a dictionary's metadata, create a DICT structure.
+function makeDict(meta, attrs, strings) {
+    var m = {};
+    for (var i = 0; i < meta.length; i += 1) {
+        var entry = meta[i];
+        var value = attrs[entry.name];
+        if (value !== undefined && !equals(value, entry.value)) {
+            if (entry.type === 'SID') {
+                value = encodeString(value, strings);
+            }
+
+            m[entry.op] = {name: entry.name, type: entry.type, value: value};
+        }
+    }
+
+    return m;
+}
+
+// The Top DICT houses the global font attributes.
+function makeTopDict(attrs, strings) {
+    var t = new table.Table('Top DICT', [
+        {name: 'dict', type: 'DICT', value: {}}
+    ]);
+    t.dict = makeDict(TOP_DICT_META, attrs, strings);
+    return t;
+}
+
+function makeTopDictIndex(topDict) {
+    var t = new table.Table('Top DICT INDEX', [
+        {name: 'topDicts', type: 'INDEX', value: []}
+    ]);
+    t.topDicts = [{name: 'topDict_0', type: 'TABLE', value: topDict}];
+    return t;
+}
+
+function makeStringIndex(strings) {
+    var t = new table.Table('String INDEX', [
+        {name: 'strings', type: 'INDEX', value: []}
+    ]);
+    t.strings = [];
+    for (var i = 0; i < strings.length; i += 1) {
+        t.strings.push({name: 'string_' + i, type: 'STRING', value: strings[i]});
+    }
+
+    return t;
+}
+
+function makeGlobalSubrIndex() {
+    // Currently we don't use subroutines.
+    return new table.Table('Global Subr INDEX', [
+        {name: 'subrs', type: 'INDEX', value: []}
+    ]);
+}
+
+function makeCharsets(glyphNames, strings) {
+    var t = new table.Table('Charsets', [
+        {name: 'format', type: 'Card8', value: 0}
+    ]);
+    for (var i = 0; i < glyphNames.length; i += 1) {
+        var glyphName = glyphNames[i];
+        var glyphSID = encodeString(glyphName, strings);
+        t.fields.push({name: 'glyph_' + i, type: 'SID', value: glyphSID});
+    }
+
+    return t;
+}
+
+function glyphToOps(glyph) {
+    var ops = [];
+    var path = glyph.path;
+    ops.push({name: 'width', type: 'NUMBER', value: glyph.advanceWidth});
+    var x = 0;
+    var y = 0;
+    for (var i = 0; i < path.commands.length; i += 1) {
+        var dx;
+        var dy;
+        var cmd = path.commands[i];
+        if (cmd.type === 'Q') {
+            // CFF only supports bézier curves, so convert the quad to a bézier.
+            var _13 = 1 / 3;
+            var _23 = 2 / 3;
+
+            // We're going to create a new command so we don't change the original path.
+            cmd = {
+                type: 'C',
+                x: cmd.x,
+                y: cmd.y,
+                x1: _13 * x + _23 * cmd.x1,
+                y1: _13 * y + _23 * cmd.y1,
+                x2: _13 * cmd.x + _23 * cmd.x1,
+                y2: _13 * cmd.y + _23 * cmd.y1
+            };
+        }
+
+        if (cmd.type === 'M') {
+            dx = Math.round(cmd.x - x);
+            dy = Math.round(cmd.y - y);
+            ops.push({name: 'dx', type: 'NUMBER', value: dx});
+            ops.push({name: 'dy', type: 'NUMBER', value: dy});
+            ops.push({name: 'rmoveto', type: 'OP', value: 21});
+            x = Math.round(cmd.x);
+            y = Math.round(cmd.y);
+        } else if (cmd.type === 'L') {
+            dx = Math.round(cmd.x - x);
+            dy = Math.round(cmd.y - y);
+            ops.push({name: 'dx', type: 'NUMBER', value: dx});
+            ops.push({name: 'dy', type: 'NUMBER', value: dy});
+            ops.push({name: 'rlineto', type: 'OP', value: 5});
+            x = Math.round(cmd.x);
+            y = Math.round(cmd.y);
+        } else if (cmd.type === 'C') {
+            var dx1 = Math.round(cmd.x1 - x);
+            var dy1 = Math.round(cmd.y1 - y);
+            var dx2 = Math.round(cmd.x2 - cmd.x1);
+            var dy2 = Math.round(cmd.y2 - cmd.y1);
+            dx = Math.round(cmd.x - cmd.x2);
+            dy = Math.round(cmd.y - cmd.y2);
+            ops.push({name: 'dx1', type: 'NUMBER', value: dx1});
+            ops.push({name: 'dy1', type: 'NUMBER', value: dy1});
+            ops.push({name: 'dx2', type: 'NUMBER', value: dx2});
+            ops.push({name: 'dy2', type: 'NUMBER', value: dy2});
+            ops.push({name: 'dx', type: 'NUMBER', value: dx});
+            ops.push({name: 'dy', type: 'NUMBER', value: dy});
+            ops.push({name: 'rrcurveto', type: 'OP', value: 8});
+            x = Math.round(cmd.x);
+            y = Math.round(cmd.y);
+        }
+
+        // Contours are closed automatically.
+
+    }
+
+    ops.push({name: 'endchar', type: 'OP', value: 14});
+    return ops;
+}
+
+function makeCharStringsIndex(glyphs) {
+    var t = new table.Table('CharStrings INDEX', [
+        {name: 'charStrings', type: 'INDEX', value: []}
+    ]);
+
+    for (var i = 0; i < glyphs.length; i += 1) {
+        var glyph = glyphs.get(i);
+        var ops = glyphToOps(glyph);
+        t.charStrings.push({name: glyph.name, type: 'CHARSTRING', value: ops});
+    }
+
+    return t;
+}
+
+function makePrivateDict(attrs, strings) {
+    var t = new table.Table('Private DICT', [
+        {name: 'dict', type: 'DICT', value: {}}
+    ]);
+    t.dict = makeDict(PRIVATE_DICT_META, attrs, strings);
+    return t;
+}
+
+function makePrivateDictIndex(privateDict) {
+    var t = new table.Table('Private DICT INDEX', [
+        {name: 'privateDicts', type: 'INDEX', value: []}
+    ]);
+    t.privateDicts = [{name: 'privateDict_0', type: 'TABLE', value: privateDict}];
+    return t;
+}
+
+function makeCFFTable(glyphs, options) {
+    var t = new table.Table('CFF ', [
+        {name: 'header', type: 'TABLE'},
+        {name: 'nameIndex', type: 'TABLE'},
+        {name: 'topDictIndex', type: 'TABLE'},
+        {name: 'stringIndex', type: 'TABLE'},
+        {name: 'globalSubrIndex', type: 'TABLE'},
+        {name: 'charsets', type: 'TABLE'},
+        {name: 'charStringsIndex', type: 'TABLE'},
+        {name: 'privateDictIndex', type: 'TABLE'}
+    ]);
+
+    var fontScale = 1 / options.unitsPerEm;
+    // We use non-zero values for the offsets so that the DICT encodes them.
+    // This is important because the size of the Top DICT plays a role in offset calculation,
+    // and the size shouldn't change after we've written correct offsets.
+    var attrs = {
+        version: options.version,
+        fullName: options.fullName,
+        familyName: options.familyName,
+        weight: options.weightName,
+        fontMatrix: [fontScale, 0, 0, fontScale, 0, 0],
+        charset: 999,
+        encoding: 0,
+        charStrings: 999,
+        private: [0, 999]
+    };
+
+    var privateAttrs = {};
+
+    var glyphNames = [];
+    var glyph;
+
+    // Skip first glyph (.notdef)
+    for (var i = 1; i < glyphs.length; i += 1) {
+        glyph = glyphs.get(i);
+        glyphNames.push(glyph.name);
+    }
+
+    var strings = [];
+
+    t.header = makeHeader();
+    t.nameIndex = makeNameIndex([options.postScriptName]);
+    var topDict = makeTopDict(attrs, strings);
+    t.topDictIndex = makeTopDictIndex(topDict);
+    t.globalSubrIndex = makeGlobalSubrIndex();
+    t.charsets = makeCharsets(glyphNames, strings);
+    t.charStringsIndex = makeCharStringsIndex(glyphs);
+    var privateDict = makePrivateDict(privateAttrs, strings);
+    t.privateDictIndex = makePrivateDictIndex(privateDict);
+
+    // Needs to come at the end, to encode all custom strings used in the font.
+    t.stringIndex = makeStringIndex(strings);
+
+    var startOffset = t.header.sizeOf() +
+        t.nameIndex.sizeOf() +
+        t.topDictIndex.sizeOf() +
+        t.stringIndex.sizeOf() +
+        t.globalSubrIndex.sizeOf();
+    attrs.charset = startOffset;
+
+    // We use the CFF standard encoding; proper encoding will be handled in cmap.
+    attrs.encoding = 0;
+    attrs.charStrings = attrs.charset + t.charsets.sizeOf();
+    attrs.private[1] = attrs.charStrings + t.charStringsIndex.sizeOf();
+
+    // Recreate the Top DICT INDEX with the correct offsets.
+    topDict = makeTopDict(attrs, strings);
+    t.topDictIndex = makeTopDictIndex(topDict);
+
+    return t;
+}
+
+exports.parse = parseCFFTable;
+exports.make = makeCFFTable;
+
+},{"../encoding":4,"../glyphset":7,"../parse":9,"../path":10,"../table":11}],13:[function(_dereq_,module,exports){
+// The `cmap` table stores the mappings from characters to glyphs.
+// https://www.microsoft.com/typography/OTSPEC/cmap.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the `cmap` table. This table stores the mappings from characters to glyphs.
+// There are many available formats, but we only support the Windows format 4.
+// This function returns a `CmapEncoding` object or null if no supported format could be found.
+function parseCmapTable(data, start) {
+    var i;
+    var cmap = {};
+    cmap.version = parse.getUShort(data, start);
+    check.argument(cmap.version === 0, 'cmap table version should be 0.');
+
+    // The cmap table can contain many sub-tables, each with their own format.
+    // We're only interested in a "platform 3" table. This is a Windows format.
+    cmap.numTables = parse.getUShort(data, start + 2);
+    var offset = -1;
+    for (i = 0; i < cmap.numTables; i += 1) {
+        var platformId = parse.getUShort(data, start + 4 + (i * 8));
+        var encodingId = parse.getUShort(data, start + 4 + (i * 8) + 2);
+        if (platformId === 3 && (encodingId === 1 || encodingId === 0)) {
+            offset = parse.getULong(data, start + 4 + (i * 8) + 4);
+            break;
+        }
+    }
+
+    if (offset === -1) {
+        // There is no cmap table in the font that we support, so return null.
+        // This font will be marked as unsupported.
+        return null;
+    }
+
+    var p = new parse.Parser(data, start + offset);
+    cmap.format = p.parseUShort();
+    check.argument(cmap.format === 4, 'Only format 4 cmap tables are supported.');
+
+    // Length in bytes of the sub-tables.
+    cmap.length = p.parseUShort();
+    cmap.language = p.parseUShort();
+
+    // segCount is stored x 2.
+    var segCount;
+    cmap.segCount = segCount = p.parseUShort() >> 1;
+
+    // Skip searchRange, entrySelector, rangeShift.
+    p.skip('uShort', 3);
+
+    // The "unrolled" mapping from character codes to glyph indices.
+    cmap.glyphIndexMap = {};
+
+    var endCountParser = new parse.Parser(data, start + offset + 14);
+    var startCountParser = new parse.Parser(data, start + offset + 16 + segCount * 2);
+    var idDeltaParser = new parse.Parser(data, start + offset + 16 + segCount * 4);
+    var idRangeOffsetParser = new parse.Parser(data, start + offset + 16 + segCount * 6);
+    var glyphIndexOffset = start + offset + 16 + segCount * 8;
+    for (i = 0; i < segCount - 1; i += 1) {
+        var glyphIndex;
+        var endCount = endCountParser.parseUShort();
+        var startCount = startCountParser.parseUShort();
+        var idDelta = idDeltaParser.parseShort();
+        var idRangeOffset = idRangeOffsetParser.parseUShort();
+        for (var c = startCount; c <= endCount; c += 1) {
+            if (idRangeOffset !== 0) {
+                // The idRangeOffset is relative to the current position in the idRangeOffset array.
+                // Take the current offset in the idRangeOffset array.
+                glyphIndexOffset = (idRangeOffsetParser.offset + idRangeOffsetParser.relativeOffset - 2);
+
+                // Add the value of the idRangeOffset, which will move us into the glyphIndex array.
+                glyphIndexOffset += idRangeOffset;
+
+                // Then add the character index of the current segment, multiplied by 2 for USHORTs.
+                glyphIndexOffset += (c - startCount) * 2;
+                glyphIndex = parse.getUShort(data, glyphIndexOffset);
+                if (glyphIndex !== 0) {
+                    glyphIndex = (glyphIndex + idDelta) & 0xFFFF;
+                }
+            } else {
+                glyphIndex = (c + idDelta) & 0xFFFF;
+            }
+
+            cmap.glyphIndexMap[c] = glyphIndex;
+        }
+    }
+
+    return cmap;
+}
+
+function addSegment(t, code, glyphIndex) {
+    t.segments.push({
+        end: code,
+        start: code,
+        delta: -(code - glyphIndex),
+        offset: 0
+    });
+}
+
+function addTerminatorSegment(t) {
+    t.segments.push({
+        end: 0xFFFF,
+        start: 0xFFFF,
+        delta: 1,
+        offset: 0
+    });
+}
+
+function makeCmapTable(glyphs) {
+    var i;
+    var t = new table.Table('cmap', [
+        {name: 'version', type: 'USHORT', value: 0},
+        {name: 'numTables', type: 'USHORT', value: 1},
+        {name: 'platformID', type: 'USHORT', value: 3},
+        {name: 'encodingID', type: 'USHORT', value: 1},
+        {name: 'offset', type: 'ULONG', value: 12},
+        {name: 'format', type: 'USHORT', value: 4},
+        {name: 'length', type: 'USHORT', value: 0},
+        {name: 'language', type: 'USHORT', value: 0},
+        {name: 'segCountX2', type: 'USHORT', value: 0},
+        {name: 'searchRange', type: 'USHORT', value: 0},
+        {name: 'entrySelector', type: 'USHORT', value: 0},
+        {name: 'rangeShift', type: 'USHORT', value: 0}
+    ]);
+
+    t.segments = [];
+    for (i = 0; i < glyphs.length; i += 1) {
+        var glyph = glyphs.get(i);
+        for (var j = 0; j < glyph.unicodes.length; j += 1) {
+            addSegment(t, glyph.unicodes[j], i);
+        }
+
+        t.segments = t.segments.sort(function(a, b) {
+            return a.start - b.start;
+        });
+    }
+
+    addTerminatorSegment(t);
+
+    var segCount;
+    segCount = t.segments.length;
+    t.segCountX2 = segCount * 2;
+    t.searchRange = Math.pow(2, Math.floor(Math.log(segCount) / Math.log(2))) * 2;
+    t.entrySelector = Math.log(t.searchRange / 2) / Math.log(2);
+    t.rangeShift = t.segCountX2 - t.searchRange;
+
+    // Set up parallel segment arrays.
+    var endCounts = [];
+    var startCounts = [];
+    var idDeltas = [];
+    var idRangeOffsets = [];
+    var glyphIds = [];
+
+    for (i = 0; i < segCount; i += 1) {
+        var segment = t.segments[i];
+        endCounts = endCounts.concat({name: 'end_' + i, type: 'USHORT', value: segment.end});
+        startCounts = startCounts.concat({name: 'start_' + i, type: 'USHORT', value: segment.start});
+        idDeltas = idDeltas.concat({name: 'idDelta_' + i, type: 'SHORT', value: segment.delta});
+        idRangeOffsets = idRangeOffsets.concat({name: 'idRangeOffset_' + i, type: 'USHORT', value: segment.offset});
+        if (segment.glyphId !== undefined) {
+            glyphIds = glyphIds.concat({name: 'glyph_' + i, type: 'USHORT', value: segment.glyphId});
+        }
+    }
+
+    t.fields = t.fields.concat(endCounts);
+    t.fields.push({name: 'reservedPad', type: 'USHORT', value: 0});
+    t.fields = t.fields.concat(startCounts);
+    t.fields = t.fields.concat(idDeltas);
+    t.fields = t.fields.concat(idRangeOffsets);
+    t.fields = t.fields.concat(glyphIds);
+
+    t.length = 14 + // Subtable header
+        endCounts.length * 2 +
+        2 + // reservedPad
+        startCounts.length * 2 +
+        idDeltas.length * 2 +
+        idRangeOffsets.length * 2 +
+        glyphIds.length * 2;
+
+    return t;
+}
+
+exports.parse = parseCmapTable;
+exports.make = makeCmapTable;
+
+},{"../check":2,"../parse":9,"../table":11}],14:[function(_dereq_,module,exports){
+// The `glyf` table describes the glyphs in TrueType outline format.
+// http://www.microsoft.com/typography/otspec/glyf.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var glyphset = _dereq_('../glyphset');
+var parse = _dereq_('../parse');
+var path = _dereq_('../path');
+
+// Parse the coordinate data for a glyph.
+function parseGlyphCoordinate(p, flag, previousValue, shortVectorBitMask, sameBitMask) {
+    var v;
+    if ((flag & shortVectorBitMask) > 0) {
+        // The coordinate is 1 byte long.
+        v = p.parseByte();
+        // The `same` bit is re-used for short values to signify the sign of the value.
+        if ((flag & sameBitMask) === 0) {
+            v = -v;
+        }
+
+        v = previousValue + v;
+    } else {
+        //  The coordinate is 2 bytes long.
+        // If the `same` bit is set, the coordinate is the same as the previous coordinate.
+        if ((flag & sameBitMask) > 0) {
+            v = previousValue;
+        } else {
+            // Parse the coordinate as a signed 16-bit delta value.
+            v = previousValue + p.parseShort();
+        }
+    }
+
+    return v;
+}
+
+// Parse a TrueType glyph.
+function parseGlyph(glyph, data, start) {
+    var p = new parse.Parser(data, start);
+    glyph.numberOfContours = p.parseShort();
+    glyph.xMin = p.parseShort();
+    glyph.yMin = p.parseShort();
+    glyph.xMax = p.parseShort();
+    glyph.yMax = p.parseShort();
+    var flags;
+    var flag;
+    if (glyph.numberOfContours > 0) {
+        var i;
+        // This glyph is not a composite.
+        var endPointIndices = glyph.endPointIndices = [];
+        for (i = 0; i < glyph.numberOfContours; i += 1) {
+            endPointIndices.push(p.parseUShort());
+        }
+
+        glyph.instructionLength = p.parseUShort();
+        glyph.instructions = [];
+        for (i = 0; i < glyph.instructionLength; i += 1) {
+            glyph.instructions.push(p.parseByte());
+        }
+
+        var numberOfCoordinates = endPointIndices[endPointIndices.length - 1] + 1;
+        flags = [];
+        for (i = 0; i < numberOfCoordinates; i += 1) {
+            flag = p.parseByte();
+            flags.push(flag);
+            // If bit 3 is set, we repeat this flag n times, where n is the next byte.
+            if ((flag & 8) > 0) {
+                var repeatCount = p.parseByte();
+                for (var j = 0; j < repeatCount; j += 1) {
+                    flags.push(flag);
+                    i += 1;
+                }
+            }
+        }
+
+        check.argument(flags.length === numberOfCoordinates, 'Bad flags.');
+
+        if (endPointIndices.length > 0) {
+            var points = [];
+            var point;
+            // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0.
+            if (numberOfCoordinates > 0) {
+                for (i = 0; i < numberOfCoordinates; i += 1) {
+                    flag = flags[i];
+                    point = {};
+                    point.onCurve = !!(flag & 1);
+                    point.lastPointOfContour = endPointIndices.indexOf(i) >= 0;
+                    points.push(point);
+                }
+
+                var px = 0;
+                for (i = 0; i < numberOfCoordinates; i += 1) {
+                    flag = flags[i];
+                    point = points[i];
+                    point.x = parseGlyphCoordinate(p, flag, px, 2, 16);
+                    px = point.x;
+                }
+
+                var py = 0;
+                for (i = 0; i < numberOfCoordinates; i += 1) {
+                    flag = flags[i];
+                    point = points[i];
+                    point.y = parseGlyphCoordinate(p, flag, py, 4, 32);
+                    py = point.y;
+                }
+            }
+
+            glyph.points = points;
+        } else {
+            glyph.points = [];
+        }
+    } else if (glyph.numberOfContours === 0) {
+        glyph.points = [];
+    } else {
+        glyph.isComposite = true;
+        glyph.points = [];
+        glyph.components = [];
+        var moreComponents = true;
+        while (moreComponents) {
+            flags = p.parseUShort();
+            var component = {
+                glyphIndex: p.parseUShort(),
+                xScale: 1,
+                scale01: 0,
+                scale10: 0,
+                yScale: 1,
+                dx: 0,
+                dy: 0
+            };
+            if ((flags & 1) > 0) {
+                // The arguments are words
+                component.dx = p.parseShort();
+                component.dy = p.parseShort();
+            } else {
+                // The arguments are bytes
+                component.dx = p.parseChar();
+                component.dy = p.parseChar();
+            }
+
+            if ((flags & 8) > 0) {
+                // We have a scale
+                component.xScale = component.yScale = p.parseF2Dot14();
+            } else if ((flags & 64) > 0) {
+                // We have an X / Y scale
+                component.xScale = p.parseF2Dot14();
+                component.yScale = p.parseF2Dot14();
+            } else if ((flags & 128) > 0) {
+                // We have a 2x2 transformation
+                component.xScale = p.parseF2Dot14();
+                component.scale01 = p.parseF2Dot14();
+                component.scale10 = p.parseF2Dot14();
+                component.yScale = p.parseF2Dot14();
+            }
+
+            glyph.components.push(component);
+            moreComponents = !!(flags & 32);
+        }
+    }
+}
+
+// Transform an array of points and return a new array.
+function transformPoints(points, transform) {
+    var newPoints = [];
+    for (var i = 0; i < points.length; i += 1) {
+        var pt = points[i];
+        var newPt = {
+            x: transform.xScale * pt.x + transform.scale01 * pt.y + transform.dx,
+            y: transform.scale10 * pt.x + transform.yScale * pt.y + transform.dy,
+            onCurve: pt.onCurve,
+            lastPointOfContour: pt.lastPointOfContour
+        };
+        newPoints.push(newPt);
+    }
+
+    return newPoints;
+}
+
+function getContours(points) {
+    var contours = [];
+    var currentContour = [];
+    for (var i = 0; i < points.length; i += 1) {
+        var pt = points[i];
+        currentContour.push(pt);
+        if (pt.lastPointOfContour) {
+            contours.push(currentContour);
+            currentContour = [];
+        }
+    }
+
+    check.argument(currentContour.length === 0, 'There are still points left in the current contour.');
+    return contours;
+}
+
+// Convert the TrueType glyph outline to a Path.
+function getPath(points) {
+    var p = new path.Path();
+    if (!points) {
+        return p;
+    }
+
+    var contours = getContours(points);
+    for (var i = 0; i < contours.length; i += 1) {
+        var contour = contours[i];
+        var firstPt = contour[0];
+        var lastPt = contour[contour.length - 1];
+        var curvePt;
+        var realFirstPoint;
+        if (firstPt.onCurve) {
+            curvePt = null;
+            // The first point will be consumed by the moveTo command,
+            // so skip it in the loop.
+            realFirstPoint = true;
+        } else {
+            if (lastPt.onCurve) {
+                // If the first point is off-curve and the last point is on-curve,
+                // start at the last point.
+                firstPt = lastPt;
+            } else {
+                // If both first and last points are off-curve, start at their middle.
+                firstPt = { x: (firstPt.x + lastPt.x) / 2, y: (firstPt.y + lastPt.y) / 2 };
+            }
+
+            curvePt = firstPt;
+            // The first point is synthesized, so don't skip the real first point.
+            realFirstPoint = false;
+        }
+
+        p.moveTo(firstPt.x, firstPt.y);
+
+        for (var j = realFirstPoint ? 1 : 0; j < contour.length; j += 1) {
+            var pt = contour[j];
+            var prevPt = j === 0 ? firstPt : contour[j - 1];
+            if (prevPt.onCurve && pt.onCurve) {
+                // This is a straight line.
+                p.lineTo(pt.x, pt.y);
+            } else if (prevPt.onCurve && !pt.onCurve) {
+                curvePt = pt;
+            } else if (!prevPt.onCurve && !pt.onCurve) {
+                var midPt = { x: (prevPt.x + pt.x) / 2, y: (prevPt.y + pt.y) / 2 };
+                p.quadraticCurveTo(prevPt.x, prevPt.y, midPt.x, midPt.y);
+                curvePt = pt;
+            } else if (!prevPt.onCurve && pt.onCurve) {
+                // Previous point off-curve, this point on-curve.
+                p.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
+                curvePt = null;
+            } else {
+                throw new Error('Invalid state.');
+            }
+        }
+
+        if (firstPt !== lastPt) {
+            // Connect the last and first points
+            if (curvePt) {
+                p.quadraticCurveTo(curvePt.x, curvePt.y, firstPt.x, firstPt.y);
+            } else {
+                p.lineTo(firstPt.x, firstPt.y);
+            }
+        }
+    }
+
+    p.closePath();
+    return p;
+}
+
+function buildPath(glyphs, glyph) {
+    if (glyph.isComposite) {
+        for (var j = 0; j < glyph.components.length; j += 1) {
+            var component = glyph.components[j];
+            var componentGlyph = glyphs.get(component.glyphIndex);
+            if (componentGlyph.points) {
+                var transformedPoints = transformPoints(componentGlyph.points, component);
+                glyph.points = glyph.points.concat(transformedPoints);
+            }
+        }
+    }
+
+    return getPath(glyph.points);
+}
+
+// Parse all the glyphs according to the offsets from the `loca` table.
+function parseGlyfTable(data, start, loca, font) {
+    var glyphs = new glyphset.GlyphSet(font);
+    var i;
+
+    // The last element of the loca table is invalid.
+    for (i = 0; i < loca.length - 1; i += 1) {
+        var offset = loca[i];
+        var nextOffset = loca[i + 1];
+        if (offset !== nextOffset) {
+            glyphs.push(i, glyphset.ttfGlyphLoader(font, i, parseGlyph, data, start + offset, buildPath));
+        } else {
+            glyphs.push(i, glyphset.glyphLoader(font, i));
+        }
+    }
+
+    return glyphs;
+}
+
+exports.parse = parseGlyfTable;
+
+},{"../check":2,"../glyphset":7,"../parse":9,"../path":10}],15:[function(_dereq_,module,exports){
+// The `GPOS` table contains kerning pairs, among other things.
+// https://www.microsoft.com/typography/OTSPEC/gpos.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+
+// Parse ScriptList and FeatureList tables of GPOS, GSUB, GDEF, BASE, JSTF tables.
+// These lists are unused by now, this function is just the basis for a real parsing.
+function parseTaggedListTable(data, start) {
+    var p = new parse.Parser(data, start);
+    var n = p.parseUShort();
+    var list = [];
+    for (var i = 0; i < n; i++) {
+        list[p.parseTag()] = { offset: p.parseUShort() };
+    }
+
+    return list;
+}
+
+// Parse a coverage table in a GSUB, GPOS or GDEF table.
+// Format 1 is a simple list of glyph ids,
+// Format 2 is a list of ranges. It is expanded in a list of glyphs, maybe not the best idea.
+function parseCoverageTable(data, start) {
+    var p = new parse.Parser(data, start);
+    var format = p.parseUShort();
+    var count =  p.parseUShort();
+    if (format === 1) {
+        return p.parseUShortList(count);
+    }
+    else if (format === 2) {
+        var coverage = [];
+        for (; count--;) {
+            var begin = p.parseUShort();
+            var end = p.parseUShort();
+            var index = p.parseUShort();
+            for (var i = begin; i <= end; i++) {
+                coverage[index++] = i;
+            }
+        }
+
+        return coverage;
+    }
+}
+
+// Parse a Class Definition Table in a GSUB, GPOS or GDEF table.
+// Returns a function that gets a class value from a glyph ID.
+function parseClassDefTable(data, start) {
+    var p = new parse.Parser(data, start);
+    var format = p.parseUShort();
+    if (format === 1) {
+        // Format 1 specifies a range of consecutive glyph indices, one class per glyph ID.
+        var startGlyph = p.parseUShort();
+        var glyphCount = p.parseUShort();
+        var classes = p.parseUShortList(glyphCount);
+        return function(glyphID) {
+            return classes[glyphID - startGlyph] || 0;
+        };
+    }
+    else if (format === 2) {
+        // Format 2 defines multiple groups of glyph indices that belong to the same class.
+        var rangeCount = p.parseUShort();
+        var startGlyphs = [];
+        var endGlyphs = [];
+        var classValues = [];
+        for (var i = 0; i < rangeCount; i++) {
+            startGlyphs[i] = p.parseUShort();
+            endGlyphs[i] = p.parseUShort();
+            classValues[i] = p.parseUShort();
+        }
+
+        return function(glyphID) {
+            var l = 0;
+            var r = startGlyphs.length - 1;
+            while (l < r) {
+                var c = (l + r + 1) >> 1;
+                if (glyphID < startGlyphs[c]) {
+                    r = c - 1;
+                } else {
+                    l = c;
+                }
+            }
+
+            if (startGlyphs[l] <= glyphID && glyphID <= endGlyphs[l]) {
+                return classValues[l] || 0;
+            }
+
+            return 0;
+        };
+    }
+}
+
+// Parse a pair adjustment positioning subtable, format 1 or format 2
+// The subtable is returned in the form of a lookup function.
+function parsePairPosSubTable(data, start) {
+    var p = new parse.Parser(data, start);
+    // This part is common to format 1 and format 2 subtables
+    var format = p.parseUShort();
+    var coverageOffset = p.parseUShort();
+    var coverage = parseCoverageTable(data, start + coverageOffset);
+    // valueFormat 4: XAdvance only, 1: XPlacement only, 0: no ValueRecord for second glyph
+    // Only valueFormat1=4 and valueFormat2=0 is supported.
+    var valueFormat1 = p.parseUShort();
+    var valueFormat2 = p.parseUShort();
+    var value1;
+    var value2;
+    if (valueFormat1 !== 4 || valueFormat2 !== 0) return;
+    var sharedPairSets = {};
+    if (format === 1) {
+        // Pair Positioning Adjustment: Format 1
+        var pairSetCount = p.parseUShort();
+        var pairSet = [];
+        // Array of offsets to PairSet tables-from beginning of PairPos subtable-ordered by Coverage Index
+        var pairSetOffsets = p.parseOffset16List(pairSetCount);
+        for (var firstGlyph = 0; firstGlyph < pairSetCount; firstGlyph++) {
+            var pairSetOffset = pairSetOffsets[firstGlyph];
+            var sharedPairSet = sharedPairSets[pairSetOffset];
+            if (!sharedPairSet) {
+                // Parse a pairset table in a pair adjustment subtable format 1
+                sharedPairSet = {};
+                p.relativeOffset = pairSetOffset;
+                var pairValueCount = p.parseUShort();
+                for (; pairValueCount--;) {
+                    var secondGlyph = p.parseUShort();
+                    if (valueFormat1) value1 = p.parseShort();
+                    if (valueFormat2) value2 = p.parseShort();
+                    // We only support valueFormat1 = 4 and valueFormat2 = 0,
+                    // so value1 is the XAdvance and value2 is empty.
+                    sharedPairSet[secondGlyph] = value1;
+                }
+            }
+
+            pairSet[coverage[firstGlyph]] = sharedPairSet;
+        }
+
+        return function(leftGlyph, rightGlyph) {
+            var pairs = pairSet[leftGlyph];
+            if (pairs) return pairs[rightGlyph];
+        };
+    }
+    else if (format === 2) {
+        // Pair Positioning Adjustment: Format 2
+        var classDef1Offset = p.parseUShort();
+        var classDef2Offset = p.parseUShort();
+        var class1Count = p.parseUShort();
+        var class2Count = p.parseUShort();
+        var getClass1 = parseClassDefTable(data, start + classDef1Offset);
+        var getClass2 = parseClassDefTable(data, start + classDef2Offset);
+
+        // Parse kerning values by class pair.
+        var kerningMatrix = [];
+        for (var i = 0; i < class1Count; i++) {
+            var kerningRow = kerningMatrix[i] = [];
+            for (var j = 0; j < class2Count; j++) {
+                if (valueFormat1) value1 = p.parseShort();
+                if (valueFormat2) value2 = p.parseShort();
+                // We only support valueFormat1 = 4 and valueFormat2 = 0,
+                // so value1 is the XAdvance and value2 is empty.
+                kerningRow[j] = value1;
+            }
+        }
+
+        // Convert coverage list to a hash
+        var covered = {};
+        for (i = 0; i < coverage.length; i++) covered[coverage[i]] = 1;
+
+        // Get the kerning value for a specific glyph pair.
+        return function(leftGlyph, rightGlyph) {
+            if (!covered[leftGlyph]) return;
+            var class1 = getClass1(leftGlyph);
+            var class2 = getClass2(rightGlyph);
+            var kerningRow = kerningMatrix[class1];
+
+            if (kerningRow) {
+                return kerningRow[class2];
+            }
+        };
+    }
+}
+
+// Parse a LookupTable (present in of GPOS, GSUB, GDEF, BASE, JSTF tables).
+function parseLookupTable(data, start) {
+    var p = new parse.Parser(data, start);
+    var lookupType = p.parseUShort();
+    var lookupFlag = p.parseUShort();
+    var useMarkFilteringSet = lookupFlag & 0x10;
+    var subTableCount = p.parseUShort();
+    var subTableOffsets = p.parseOffset16List(subTableCount);
+    var table = {
+        lookupType: lookupType,
+        lookupFlag: lookupFlag,
+        markFilteringSet: useMarkFilteringSet ? p.parseUShort() : -1
+    };
+    // LookupType 2, Pair adjustment
+    if (lookupType === 2) {
+        var subtables = [];
+        for (var i = 0; i < subTableCount; i++) {
+            subtables.push(parsePairPosSubTable(data, start + subTableOffsets[i]));
+        }
+        // Return a function which finds the kerning values in the subtables.
+        table.getKerningValue = function(leftGlyph, rightGlyph) {
+            for (var i = subtables.length; i--;) {
+                var value = subtables[i](leftGlyph, rightGlyph);
+                if (value !== undefined) return value;
+            }
+
+            return 0;
+        };
+    }
+
+    return table;
+}
+
+// Parse the `GPOS` table which contains, among other things, kerning pairs.
+// https://www.microsoft.com/typography/OTSPEC/gpos.htm
+function parseGposTable(data, start, font) {
+    var p = new parse.Parser(data, start);
+    var tableVersion = p.parseFixed();
+    check.argument(tableVersion === 1, 'Unsupported GPOS table version.');
+
+    // ScriptList and FeatureList - ignored for now
+    parseTaggedListTable(data, start + p.parseUShort());
+    // 'kern' is the feature we are looking for.
+    parseTaggedListTable(data, start + p.parseUShort());
+
+    // LookupList
+    var lookupListOffset = p.parseUShort();
+    p.relativeOffset = lookupListOffset;
+    var lookupCount = p.parseUShort();
+    var lookupTableOffsets = p.parseOffset16List(lookupCount);
+    var lookupListAbsoluteOffset = start + lookupListOffset;
+    for (var i = 0; i < lookupCount; i++) {
+        var table = parseLookupTable(data, lookupListAbsoluteOffset + lookupTableOffsets[i]);
+        if (table.lookupType === 2 && !font.getGposKerningValue) font.getGposKerningValue = table.getKerningValue;
+    }
+}
+
+exports.parse = parseGposTable;
+
+},{"../check":2,"../parse":9}],16:[function(_dereq_,module,exports){
+// The `head` table contains global information about the font.
+// https://www.microsoft.com/typography/OTSPEC/head.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the header `head` table
+function parseHeadTable(data, start) {
+    var head = {};
+    var p = new parse.Parser(data, start);
+    head.version = p.parseVersion();
+    head.fontRevision = Math.round(p.parseFixed() * 1000) / 1000;
+    head.checkSumAdjustment = p.parseULong();
+    head.magicNumber = p.parseULong();
+    check.argument(head.magicNumber === 0x5F0F3CF5, 'Font header has wrong magic number.');
+    head.flags = p.parseUShort();
+    head.unitsPerEm = p.parseUShort();
+    head.created = p.parseLongDateTime();
+    head.modified = p.parseLongDateTime();
+    head.xMin = p.parseShort();
+    head.yMin = p.parseShort();
+    head.xMax = p.parseShort();
+    head.yMax = p.parseShort();
+    head.macStyle = p.parseUShort();
+    head.lowestRecPPEM = p.parseUShort();
+    head.fontDirectionHint = p.parseShort();
+    head.indexToLocFormat = p.parseShort();     // 50
+    head.glyphDataFormat = p.parseShort();
+    return head;
+}
+
+function makeHeadTable(options) {
+    return new table.Table('head', [
+        {name: 'version', type: 'FIXED', value: 0x00010000},
+        {name: 'fontRevision', type: 'FIXED', value: 0x00010000},
+        {name: 'checkSumAdjustment', type: 'ULONG', value: 0},
+        {name: 'magicNumber', type: 'ULONG', value: 0x5F0F3CF5},
+        {name: 'flags', type: 'USHORT', value: 0},
+        {name: 'unitsPerEm', type: 'USHORT', value: 1000},
+        {name: 'created', type: 'LONGDATETIME', value: 0},
+        {name: 'modified', type: 'LONGDATETIME', value: 0},
+        {name: 'xMin', type: 'SHORT', value: 0},
+        {name: 'yMin', type: 'SHORT', value: 0},
+        {name: 'xMax', type: 'SHORT', value: 0},
+        {name: 'yMax', type: 'SHORT', value: 0},
+        {name: 'macStyle', type: 'USHORT', value: 0},
+        {name: 'lowestRecPPEM', type: 'USHORT', value: 0},
+        {name: 'fontDirectionHint', type: 'SHORT', value: 2},
+        {name: 'indexToLocFormat', type: 'SHORT', value: 0},
+        {name: 'glyphDataFormat', type: 'SHORT', value: 0}
+    ], options);
+}
+
+exports.parse = parseHeadTable;
+exports.make = makeHeadTable;
+
+},{"../check":2,"../parse":9,"../table":11}],17:[function(_dereq_,module,exports){
+// The `hhea` table contains information for horizontal layout.
+// https://www.microsoft.com/typography/OTSPEC/hhea.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the horizontal header `hhea` table
+function parseHheaTable(data, start) {
+    var hhea = {};
+    var p = new parse.Parser(data, start);
+    hhea.version = p.parseVersion();
+    hhea.ascender = p.parseShort();
+    hhea.descender = p.parseShort();
+    hhea.lineGap = p.parseShort();
+    hhea.advanceWidthMax = p.parseUShort();
+    hhea.minLeftSideBearing = p.parseShort();
+    hhea.minRightSideBearing = p.parseShort();
+    hhea.xMaxExtent = p.parseShort();
+    hhea.caretSlopeRise = p.parseShort();
+    hhea.caretSlopeRun = p.parseShort();
+    hhea.caretOffset = p.parseShort();
+    p.relativeOffset += 8;
+    hhea.metricDataFormat = p.parseShort();
+    hhea.numberOfHMetrics = p.parseUShort();
+    return hhea;
+}
+
+function makeHheaTable(options) {
+    return new table.Table('hhea', [
+        {name: 'version', type: 'FIXED', value: 0x00010000},
+        {name: 'ascender', type: 'FWORD', value: 0},
+        {name: 'descender', type: 'FWORD', value: 0},
+        {name: 'lineGap', type: 'FWORD', value: 0},
+        {name: 'advanceWidthMax', type: 'UFWORD', value: 0},
+        {name: 'minLeftSideBearing', type: 'FWORD', value: 0},
+        {name: 'minRightSideBearing', type: 'FWORD', value: 0},
+        {name: 'xMaxExtent', type: 'FWORD', value: 0},
+        {name: 'caretSlopeRise', type: 'SHORT', value: 1},
+        {name: 'caretSlopeRun', type: 'SHORT', value: 0},
+        {name: 'caretOffset', type: 'SHORT', value: 0},
+        {name: 'reserved1', type: 'SHORT', value: 0},
+        {name: 'reserved2', type: 'SHORT', value: 0},
+        {name: 'reserved3', type: 'SHORT', value: 0},
+        {name: 'reserved4', type: 'SHORT', value: 0},
+        {name: 'metricDataFormat', type: 'SHORT', value: 0},
+        {name: 'numberOfHMetrics', type: 'USHORT', value: 0}
+    ], options);
+}
+
+exports.parse = parseHheaTable;
+exports.make = makeHheaTable;
+
+},{"../parse":9,"../table":11}],18:[function(_dereq_,module,exports){
+// The `hmtx` table contains the horizontal metrics for all glyphs.
+// https://www.microsoft.com/typography/OTSPEC/hmtx.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the `hmtx` table, which contains the horizontal metrics for all glyphs.
+// This function augments the glyph array, adding the advanceWidth and leftSideBearing to each glyph.
+function parseHmtxTable(data, start, numMetrics, numGlyphs, glyphs) {
+    var advanceWidth;
+    var leftSideBearing;
+    var p = new parse.Parser(data, start);
+    for (var i = 0; i < numGlyphs; i += 1) {
+        // If the font is monospaced, only one entry is needed. This last entry applies to all subsequent glyphs.
+        if (i < numMetrics) {
+            advanceWidth = p.parseUShort();
+            leftSideBearing = p.parseShort();
+        }
+
+        var glyph = glyphs.get(i);
+        glyph.advanceWidth = advanceWidth;
+        glyph.leftSideBearing = leftSideBearing;
+    }
+}
+
+function makeHmtxTable(glyphs) {
+    var t = new table.Table('hmtx', []);
+    for (var i = 0; i < glyphs.length; i += 1) {
+        var glyph = glyphs.get(i);
+        var advanceWidth = glyph.advanceWidth || 0;
+        var leftSideBearing = glyph.leftSideBearing || 0;
+        t.fields.push({name: 'advanceWidth_' + i, type: 'USHORT', value: advanceWidth});
+        t.fields.push({name: 'leftSideBearing_' + i, type: 'SHORT', value: leftSideBearing});
+    }
+
+    return t;
+}
+
+exports.parse = parseHmtxTable;
+exports.make = makeHmtxTable;
+
+},{"../parse":9,"../table":11}],19:[function(_dereq_,module,exports){
+// The `kern` table contains kerning pairs.
+// Note that some fonts use the GPOS OpenType layout table to specify kerning.
+// https://www.microsoft.com/typography/OTSPEC/kern.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+
+// Parse the `kern` table which contains kerning pairs.
+function parseKernTable(data, start) {
+    var pairs = {};
+    var p = new parse.Parser(data, start);
+    var tableVersion = p.parseUShort();
+    check.argument(tableVersion === 0, 'Unsupported kern table version.');
+    // Skip nTables.
+    p.skip('uShort', 1);
+    var subTableVersion = p.parseUShort();
+    check.argument(subTableVersion === 0, 'Unsupported kern sub-table version.');
+    // Skip subTableLength, subTableCoverage
+    p.skip('uShort', 2);
+    var nPairs = p.parseUShort();
+    // Skip searchRange, entrySelector, rangeShift.
+    p.skip('uShort', 3);
+    for (var i = 0; i < nPairs; i += 1) {
+        var leftIndex = p.parseUShort();
+        var rightIndex = p.parseUShort();
+        var value = p.parseShort();
+        pairs[leftIndex + ',' + rightIndex] = value;
+    }
+
+    return pairs;
+}
+
+exports.parse = parseKernTable;
+
+},{"../check":2,"../parse":9}],20:[function(_dereq_,module,exports){
+// The `loca` table stores the offsets to the locations of the glyphs in the font.
+// https://www.microsoft.com/typography/OTSPEC/loca.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+
+// Parse the `loca` table. This table stores the offsets to the locations of the glyphs in the font,
+// relative to the beginning of the glyphData table.
+// The number of glyphs stored in the `loca` table is specified in the `maxp` table (under numGlyphs)
+// The loca table has two versions: a short version where offsets are stored as uShorts, and a long
+// version where offsets are stored as uLongs. The `head` table specifies which version to use
+// (under indexToLocFormat).
+function parseLocaTable(data, start, numGlyphs, shortVersion) {
+    var p = new parse.Parser(data, start);
+    var parseFn = shortVersion ? p.parseUShort : p.parseULong;
+    // There is an extra entry after the last index element to compute the length of the last glyph.
+    // That's why we use numGlyphs + 1.
+    var glyphOffsets = [];
+    for (var i = 0; i < numGlyphs + 1; i += 1) {
+        var glyphOffset = parseFn.call(p);
+        if (shortVersion) {
+            // The short table version stores the actual offset divided by 2.
+            glyphOffset *= 2;
+        }
+
+        glyphOffsets.push(glyphOffset);
+    }
+
+    return glyphOffsets;
+}
+
+exports.parse = parseLocaTable;
+
+},{"../parse":9}],21:[function(_dereq_,module,exports){
+// The `maxp` table establishes the memory requirements for the font.
+// We need it just to get the number of glyphs in the font.
+// https://www.microsoft.com/typography/OTSPEC/maxp.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the maximum profile `maxp` table.
+function parseMaxpTable(data, start) {
+    var maxp = {};
+    var p = new parse.Parser(data, start);
+    maxp.version = p.parseVersion();
+    maxp.numGlyphs = p.parseUShort();
+    if (maxp.version === 1.0) {
+        maxp.maxPoints = p.parseUShort();
+        maxp.maxContours = p.parseUShort();
+        maxp.maxCompositePoints = p.parseUShort();
+        maxp.maxCompositeContours = p.parseUShort();
+        maxp.maxZones = p.parseUShort();
+        maxp.maxTwilightPoints = p.parseUShort();
+        maxp.maxStorage = p.parseUShort();
+        maxp.maxFunctionDefs = p.parseUShort();
+        maxp.maxInstructionDefs = p.parseUShort();
+        maxp.maxStackElements = p.parseUShort();
+        maxp.maxSizeOfInstructions = p.parseUShort();
+        maxp.maxComponentElements = p.parseUShort();
+        maxp.maxComponentDepth = p.parseUShort();
+    }
+
+    return maxp;
+}
+
+function makeMaxpTable(numGlyphs) {
+    return new table.Table('maxp', [
+        {name: 'version', type: 'FIXED', value: 0x00005000},
+        {name: 'numGlyphs', type: 'USHORT', value: numGlyphs}
+    ]);
+}
+
+exports.parse = parseMaxpTable;
+exports.make = makeMaxpTable;
+
+},{"../parse":9,"../table":11}],22:[function(_dereq_,module,exports){
+// The `name` naming table.
+// https://www.microsoft.com/typography/OTSPEC/name.htm
+
+'use strict';
+
+var encode = _dereq_('../types').encode;
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// NameIDs for the name table.
+var nameTableNames = [
+    'copyright',              // 0
+    'fontFamily',             // 1
+    'fontSubfamily',          // 2
+    'uniqueID',               // 3
+    'fullName',               // 4
+    'version',                // 5
+    'postScriptName',         // 6
+    'trademark',              // 7
+    'manufacturer',           // 8
+    'designer',               // 9
+    'description',            // 10
+    'manufacturerURL',        // 11
+    'designerURL',            // 12
+    'licence',                // 13
+    'licenceURL',             // 14
+    'reserved',               // 15
+    'preferredFamily',        // 16
+    'preferredSubfamily',     // 17
+    'compatibleFullName',     // 18
+    'sampleText',             // 19
+    'postScriptFindFontName', // 20
+    'wwsFamily',              // 21
+    'wwsSubfamily'            // 22
+];
+
+// Parse the naming `name` table
+// Only Windows Unicode English names are supported.
+// Format 1 additional fields are not supported
+function parseNameTable(data, start) {
+    var name = {};
+    var p = new parse.Parser(data, start);
+    name.format = p.parseUShort();
+    var count = p.parseUShort();
+    var stringOffset = p.offset + p.parseUShort();
+    var unknownCount = 0;
+    for (var i = 0; i < count; i++) {
+        var platformID = p.parseUShort();
+        var encodingID = p.parseUShort();
+        var languageID = p.parseUShort();
+        var nameID = p.parseUShort();
+        var property = nameTableNames[nameID];
+        var byteLength = p.parseUShort();
+        var offset = p.parseUShort();
+        // platformID - encodingID - languageID standard combinations :
+        // 1 - 0 - 0 : Macintosh, Roman, English
+        // 3 - 1 - 0x409 : Windows, Unicode BMP (UCS-2), en-US
+        if (platformID === 3 && encodingID === 1 && languageID === 0x409) {
+            var codePoints = [];
+            var length = byteLength / 2;
+            for (var j = 0; j < length; j++, offset += 2) {
+                codePoints[j] = parse.getShort(data, stringOffset + offset);
+            }
+
+            var str = String.fromCharCode.apply(null, codePoints);
+            if (property) {
+                name[property] = str;
+            }
+            else {
+                unknownCount++;
+                name['unknown' + unknownCount] = str;
+            }
+        }
+
+    }
+
+    if (name.format === 1) {
+        name.langTagCount = p.parseUShort();
+    }
+
+    return name;
+}
+
+function makeNameRecord(platformID, encodingID, languageID, nameID, length, offset) {
+    return new table.Table('NameRecord', [
+        {name: 'platformID', type: 'USHORT', value: platformID},
+        {name: 'encodingID', type: 'USHORT', value: encodingID},
+        {name: 'languageID', type: 'USHORT', value: languageID},
+        {name: 'nameID', type: 'USHORT', value: nameID},
+        {name: 'length', type: 'USHORT', value: length},
+        {name: 'offset', type: 'USHORT', value: offset}
+    ]);
+}
+
+function addMacintoshNameRecord(t, recordID, s, offset) {
+    // Macintosh, Roman, English
+    var stringBytes = encode.STRING(s);
+    t.records.push(makeNameRecord(1, 0, 0, recordID, stringBytes.length, offset));
+    t.strings.push(stringBytes);
+    offset += stringBytes.length;
+    return offset;
+}
+
+function addWindowsNameRecord(t, recordID, s, offset) {
+    // Windows, Unicode BMP (UCS-2), US English
+    var utf16Bytes = encode.UTF16(s);
+    t.records.push(makeNameRecord(3, 1, 0x0409, recordID, utf16Bytes.length, offset));
+    t.strings.push(utf16Bytes);
+    offset += utf16Bytes.length;
+    return offset;
+}
+
+function makeNameTable(options) {
+    var t = new table.Table('name', [
+        {name: 'format', type: 'USHORT', value: 0},
+        {name: 'count', type: 'USHORT', value: 0},
+        {name: 'stringOffset', type: 'USHORT', value: 0}
+    ]);
+    t.records = [];
+    t.strings = [];
+    var offset = 0;
+    var i;
+    var s;
+    // Add Macintosh records first
+    for (i = 0; i < nameTableNames.length; i += 1) {
+        if (options[nameTableNames[i]] !== undefined) {
+            s = options[nameTableNames[i]];
+            offset = addMacintoshNameRecord(t, i, s, offset);
+        }
+    }
+    // Then add Windows records
+    for (i = 0; i < nameTableNames.length; i += 1) {
+        if (options[nameTableNames[i]] !== undefined) {
+            s = options[nameTableNames[i]];
+            offset = addWindowsNameRecord(t, i, s, offset);
+        }
+    }
+
+    t.count = t.records.length;
+    t.stringOffset = 6 + t.count * 12;
+    for (i = 0; i < t.records.length; i += 1) {
+        t.fields.push({name: 'record_' + i, type: 'TABLE', value: t.records[i]});
+    }
+
+    for (i = 0; i < t.strings.length; i += 1) {
+        t.fields.push({name: 'string_' + i, type: 'LITERAL', value: t.strings[i]});
+    }
+
+    return t;
+}
+
+exports.parse = parseNameTable;
+exports.make = makeNameTable;
+
+},{"../parse":9,"../table":11,"../types":26}],23:[function(_dereq_,module,exports){
+// The `OS/2` table contains metrics required in OpenType fonts.
+// https://www.microsoft.com/typography/OTSPEC/os2.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+var unicodeRanges = [
+    {begin: 0x0000, end: 0x007F}, // Basic Latin
+    {begin: 0x0080, end: 0x00FF}, // Latin-1 Supplement
+    {begin: 0x0100, end: 0x017F}, // Latin Extended-A
+    {begin: 0x0180, end: 0x024F}, // Latin Extended-B
+    {begin: 0x0250, end: 0x02AF}, // IPA Extensions
+    {begin: 0x02B0, end: 0x02FF}, // Spacing Modifier Letters
+    {begin: 0x0300, end: 0x036F}, // Combining Diacritical Marks
+    {begin: 0x0370, end: 0x03FF}, // Greek and Coptic
+    {begin: 0x2C80, end: 0x2CFF}, // Coptic
+    {begin: 0x0400, end: 0x04FF}, // Cyrillic
+    {begin: 0x0530, end: 0x058F}, // Armenian
+    {begin: 0x0590, end: 0x05FF}, // Hebrew
+    {begin: 0xA500, end: 0xA63F}, // Vai
+    {begin: 0x0600, end: 0x06FF}, // Arabic
+    {begin: 0x07C0, end: 0x07FF}, // NKo
+    {begin: 0x0900, end: 0x097F}, // Devanagari
+    {begin: 0x0980, end: 0x09FF}, // Bengali
+    {begin: 0x0A00, end: 0x0A7F}, // Gurmukhi
+    {begin: 0x0A80, end: 0x0AFF}, // Gujarati
+    {begin: 0x0B00, end: 0x0B7F}, // Oriya
+    {begin: 0x0B80, end: 0x0BFF}, // Tamil
+    {begin: 0x0C00, end: 0x0C7F}, // Telugu
+    {begin: 0x0C80, end: 0x0CFF}, // Kannada
+    {begin: 0x0D00, end: 0x0D7F}, // Malayalam
+    {begin: 0x0E00, end: 0x0E7F}, // Thai
+    {begin: 0x0E80, end: 0x0EFF}, // Lao
+    {begin: 0x10A0, end: 0x10FF}, // Georgian
+    {begin: 0x1B00, end: 0x1B7F}, // Balinese
+    {begin: 0x1100, end: 0x11FF}, // Hangul Jamo
+    {begin: 0x1E00, end: 0x1EFF}, // Latin Extended Additional
+    {begin: 0x1F00, end: 0x1FFF}, // Greek Extended
+    {begin: 0x2000, end: 0x206F}, // General Punctuation
+    {begin: 0x2070, end: 0x209F}, // Superscripts And Subscripts
+    {begin: 0x20A0, end: 0x20CF}, // Currency Symbol
+    {begin: 0x20D0, end: 0x20FF}, // Combining Diacritical Marks For Symbols
+    {begin: 0x2100, end: 0x214F}, // Letterlike Symbols
+    {begin: 0x2150, end: 0x218F}, // Number Forms
+    {begin: 0x2190, end: 0x21FF}, // Arrows
+    {begin: 0x2200, end: 0x22FF}, // Mathematical Operators
+    {begin: 0x2300, end: 0x23FF}, // Miscellaneous Technical
+    {begin: 0x2400, end: 0x243F}, // Control Pictures
+    {begin: 0x2440, end: 0x245F}, // Optical Character Recognition
+    {begin: 0x2460, end: 0x24FF}, // Enclosed Alphanumerics
+    {begin: 0x2500, end: 0x257F}, // Box Drawing
+    {begin: 0x2580, end: 0x259F}, // Block Elements
+    {begin: 0x25A0, end: 0x25FF}, // Geometric Shapes
+    {begin: 0x2600, end: 0x26FF}, // Miscellaneous Symbols
+    {begin: 0x2700, end: 0x27BF}, // Dingbats
+    {begin: 0x3000, end: 0x303F}, // CJK Symbols And Punctuation
+    {begin: 0x3040, end: 0x309F}, // Hiragana
+    {begin: 0x30A0, end: 0x30FF}, // Katakana
+    {begin: 0x3100, end: 0x312F}, // Bopomofo
+    {begin: 0x3130, end: 0x318F}, // Hangul Compatibility Jamo
+    {begin: 0xA840, end: 0xA87F}, // Phags-pa
+    {begin: 0x3200, end: 0x32FF}, // Enclosed CJK Letters And Months
+    {begin: 0x3300, end: 0x33FF}, // CJK Compatibility
+    {begin: 0xAC00, end: 0xD7AF}, // Hangul Syllables
+    {begin: 0xD800, end: 0xDFFF}, // Non-Plane 0 *
+    {begin: 0x10900, end: 0x1091F}, // Phoenicia
+    {begin: 0x4E00, end: 0x9FFF}, // CJK Unified Ideographs
+    {begin: 0xE000, end: 0xF8FF}, // Private Use Area (plane 0)
+    {begin: 0x31C0, end: 0x31EF}, // CJK Strokes
+    {begin: 0xFB00, end: 0xFB4F}, // Alphabetic Presentation Forms
+    {begin: 0xFB50, end: 0xFDFF}, // Arabic Presentation Forms-A
+    {begin: 0xFE20, end: 0xFE2F}, // Combining Half Marks
+    {begin: 0xFE10, end: 0xFE1F}, // Vertical Forms
+    {begin: 0xFE50, end: 0xFE6F}, // Small Form Variants
+    {begin: 0xFE70, end: 0xFEFF}, // Arabic Presentation Forms-B
+    {begin: 0xFF00, end: 0xFFEF}, // Halfwidth And Fullwidth Forms
+    {begin: 0xFFF0, end: 0xFFFF}, // Specials
+    {begin: 0x0F00, end: 0x0FFF}, // Tibetan
+    {begin: 0x0700, end: 0x074F}, // Syriac
+    {begin: 0x0780, end: 0x07BF}, // Thaana
+    {begin: 0x0D80, end: 0x0DFF}, // Sinhala
+    {begin: 0x1000, end: 0x109F}, // Myanmar
+    {begin: 0x1200, end: 0x137F}, // Ethiopic
+    {begin: 0x13A0, end: 0x13FF}, // Cherokee
+    {begin: 0x1400, end: 0x167F}, // Unified Canadian Aboriginal Syllabics
+    {begin: 0x1680, end: 0x169F}, // Ogham
+    {begin: 0x16A0, end: 0x16FF}, // Runic
+    {begin: 0x1780, end: 0x17FF}, // Khmer
+    {begin: 0x1800, end: 0x18AF}, // Mongolian
+    {begin: 0x2800, end: 0x28FF}, // Braille Patterns
+    {begin: 0xA000, end: 0xA48F}, // Yi Syllables
+    {begin: 0x1700, end: 0x171F}, // Tagalog
+    {begin: 0x10300, end: 0x1032F}, // Old Italic
+    {begin: 0x10330, end: 0x1034F}, // Gothic
+    {begin: 0x10400, end: 0x1044F}, // Deseret
+    {begin: 0x1D000, end: 0x1D0FF}, // Byzantine Musical Symbols
+    {begin: 0x1D400, end: 0x1D7FF}, // Mathematical Alphanumeric Symbols
+    {begin: 0xFF000, end: 0xFFFFD}, // Private Use (plane 15)
+    {begin: 0xFE00, end: 0xFE0F}, // Variation Selectors
+    {begin: 0xE0000, end: 0xE007F}, // Tags
+    {begin: 0x1900, end: 0x194F}, // Limbu
+    {begin: 0x1950, end: 0x197F}, // Tai Le
+    {begin: 0x1980, end: 0x19DF}, // New Tai Lue
+    {begin: 0x1A00, end: 0x1A1F}, // Buginese
+    {begin: 0x2C00, end: 0x2C5F}, // Glagolitic
+    {begin: 0x2D30, end: 0x2D7F}, // Tifinagh
+    {begin: 0x4DC0, end: 0x4DFF}, // Yijing Hexagram Symbols
+    {begin: 0xA800, end: 0xA82F}, // Syloti Nagri
+    {begin: 0x10000, end: 0x1007F}, // Linear B Syllabary
+    {begin: 0x10140, end: 0x1018F}, // Ancient Greek Numbers
+    {begin: 0x10380, end: 0x1039F}, // Ugaritic
+    {begin: 0x103A0, end: 0x103DF}, // Old Persian
+    {begin: 0x10450, end: 0x1047F}, // Shavian
+    {begin: 0x10480, end: 0x104AF}, // Osmanya
+    {begin: 0x10800, end: 0x1083F}, // Cypriot Syllabary
+    {begin: 0x10A00, end: 0x10A5F}, // Kharoshthi
+    {begin: 0x1D300, end: 0x1D35F}, // Tai Xuan Jing Symbols
+    {begin: 0x12000, end: 0x123FF}, // Cuneiform
+    {begin: 0x1D360, end: 0x1D37F}, // Counting Rod Numerals
+    {begin: 0x1B80, end: 0x1BBF}, // Sundanese
+    {begin: 0x1C00, end: 0x1C4F}, // Lepcha
+    {begin: 0x1C50, end: 0x1C7F}, // Ol Chiki
+    {begin: 0xA880, end: 0xA8DF}, // Saurashtra
+    {begin: 0xA900, end: 0xA92F}, // Kayah Li
+    {begin: 0xA930, end: 0xA95F}, // Rejang
+    {begin: 0xAA00, end: 0xAA5F}, // Cham
+    {begin: 0x10190, end: 0x101CF}, // Ancient Symbols
+    {begin: 0x101D0, end: 0x101FF}, // Phaistos Disc
+    {begin: 0x102A0, end: 0x102DF}, // Carian
+    {begin: 0x1F030, end: 0x1F09F}  // Domino Tiles
+];
+
+function getUnicodeRange(unicode) {
+    for (var i = 0; i < unicodeRanges.length; i += 1) {
+        var range = unicodeRanges[i];
+        if (unicode >= range.begin && unicode < range.end) {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+// Parse the OS/2 and Windows metrics `OS/2` table
+function parseOS2Table(data, start) {
+    var os2 = {};
+    var p = new parse.Parser(data, start);
+    os2.version = p.parseUShort();
+    os2.xAvgCharWidth = p.parseShort();
+    os2.usWeightClass = p.parseUShort();
+    os2.usWidthClass = p.parseUShort();
+    os2.fsType = p.parseUShort();
+    os2.ySubscriptXSize = p.parseShort();
+    os2.ySubscriptYSize = p.parseShort();
+    os2.ySubscriptXOffset = p.parseShort();
+    os2.ySubscriptYOffset = p.parseShort();
+    os2.ySuperscriptXSize = p.parseShort();
+    os2.ySuperscriptYSize = p.parseShort();
+    os2.ySuperscriptXOffset = p.parseShort();
+    os2.ySuperscriptYOffset = p.parseShort();
+    os2.yStrikeoutSize = p.parseShort();
+    os2.yStrikeoutPosition = p.parseShort();
+    os2.sFamilyClass = p.parseShort();
+    os2.panose = [];
+    for (var i = 0; i < 10; i++) {
+        os2.panose[i] = p.parseByte();
+    }
+
+    os2.ulUnicodeRange1 = p.parseULong();
+    os2.ulUnicodeRange2 = p.parseULong();
+    os2.ulUnicodeRange3 = p.parseULong();
+    os2.ulUnicodeRange4 = p.parseULong();
+    os2.achVendID = String.fromCharCode(p.parseByte(), p.parseByte(), p.parseByte(), p.parseByte());
+    os2.fsSelection = p.parseUShort();
+    os2.usFirstCharIndex = p.parseUShort();
+    os2.usLastCharIndex = p.parseUShort();
+    os2.sTypoAscender = p.parseShort();
+    os2.sTypoDescender = p.parseShort();
+    os2.sTypoLineGap = p.parseShort();
+    os2.usWinAscent = p.parseUShort();
+    os2.usWinDescent = p.parseUShort();
+    if (os2.version >= 1) {
+        os2.ulCodePageRange1 = p.parseULong();
+        os2.ulCodePageRange2 = p.parseULong();
+    }
+
+    if (os2.version >= 2) {
+        os2.sxHeight = p.parseShort();
+        os2.sCapHeight = p.parseShort();
+        os2.usDefaultChar = p.parseUShort();
+        os2.usBreakChar = p.parseUShort();
+        os2.usMaxContent = p.parseUShort();
+    }
+
+    return os2;
+}
+
+function makeOS2Table(options) {
+    return new table.Table('OS/2', [
+        {name: 'version', type: 'USHORT', value: 0x0003},
+        {name: 'xAvgCharWidth', type: 'SHORT', value: 0},
+        {name: 'usWeightClass', type: 'USHORT', value: 0},
+        {name: 'usWidthClass', type: 'USHORT', value: 0},
+        {name: 'fsType', type: 'USHORT', value: 0},
+        {name: 'ySubscriptXSize', type: 'SHORT', value: 650},
+        {name: 'ySubscriptYSize', type: 'SHORT', value: 699},
+        {name: 'ySubscriptXOffset', type: 'SHORT', value: 0},
+        {name: 'ySubscriptYOffset', type: 'SHORT', value: 140},
+        {name: 'ySuperscriptXSize', type: 'SHORT', value: 650},
+        {name: 'ySuperscriptYSize', type: 'SHORT', value: 699},
+        {name: 'ySuperscriptXOffset', type: 'SHORT', value: 0},
+        {name: 'ySuperscriptYOffset', type: 'SHORT', value: 479},
+        {name: 'yStrikeoutSize', type: 'SHORT', value: 49},
+        {name: 'yStrikeoutPosition', type: 'SHORT', value: 258},
+        {name: 'sFamilyClass', type: 'SHORT', value: 0},
+        {name: 'bFamilyType', type: 'BYTE', value: 0},
+        {name: 'bSerifStyle', type: 'BYTE', value: 0},
+        {name: 'bWeight', type: 'BYTE', value: 0},
+        {name: 'bProportion', type: 'BYTE', value: 0},
+        {name: 'bContrast', type: 'BYTE', value: 0},
+        {name: 'bStrokeVariation', type: 'BYTE', value: 0},
+        {name: 'bArmStyle', type: 'BYTE', value: 0},
+        {name: 'bLetterform', type: 'BYTE', value: 0},
+        {name: 'bMidline', type: 'BYTE', value: 0},
+        {name: 'bXHeight', type: 'BYTE', value: 0},
+        {name: 'ulUnicodeRange1', type: 'ULONG', value: 0},
+        {name: 'ulUnicodeRange2', type: 'ULONG', value: 0},
+        {name: 'ulUnicodeRange3', type: 'ULONG', value: 0},
+        {name: 'ulUnicodeRange4', type: 'ULONG', value: 0},
+        {name: 'achVendID', type: 'CHARARRAY', value: 'XXXX'},
+        {name: 'fsSelection', type: 'USHORT', value: 0},
+        {name: 'usFirstCharIndex', type: 'USHORT', value: 0},
+        {name: 'usLastCharIndex', type: 'USHORT', value: 0},
+        {name: 'sTypoAscender', type: 'SHORT', value: 0},
+        {name: 'sTypoDescender', type: 'SHORT', value: 0},
+        {name: 'sTypoLineGap', type: 'SHORT', value: 0},
+        {name: 'usWinAscent', type: 'USHORT', value: 0},
+        {name: 'usWinDescent', type: 'USHORT', value: 0},
+        {name: 'ulCodePageRange1', type: 'ULONG', value: 0},
+        {name: 'ulCodePageRange2', type: 'ULONG', value: 0},
+        {name: 'sxHeight', type: 'SHORT', value: 0},
+        {name: 'sCapHeight', type: 'SHORT', value: 0},
+        {name: 'usDefaultChar', type: 'USHORT', value: 0},
+        {name: 'usBreakChar', type: 'USHORT', value: 0},
+        {name: 'usMaxContext', type: 'USHORT', value: 0}
+    ], options);
+}
+
+exports.unicodeRanges = unicodeRanges;
+exports.getUnicodeRange = getUnicodeRange;
+exports.parse = parseOS2Table;
+exports.make = makeOS2Table;
+
+},{"../parse":9,"../table":11}],24:[function(_dereq_,module,exports){
+// The `post` table stores additional PostScript information, such as glyph names.
+// https://www.microsoft.com/typography/OTSPEC/post.htm
+
+'use strict';
+
+var encoding = _dereq_('../encoding');
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the PostScript `post` table
+function parsePostTable(data, start) {
+    var post = {};
+    var p = new parse.Parser(data, start);
+    var i;
+    post.version = p.parseVersion();
+    post.italicAngle = p.parseFixed();
+    post.underlinePosition = p.parseShort();
+    post.underlineThickness = p.parseShort();
+    post.isFixedPitch = p.parseULong();
+    post.minMemType42 = p.parseULong();
+    post.maxMemType42 = p.parseULong();
+    post.minMemType1 = p.parseULong();
+    post.maxMemType1 = p.parseULong();
+    switch (post.version) {
+    case 1:
+        post.names = encoding.standardNames.slice();
+        break;
+    case 2:
+        post.numberOfGlyphs = p.parseUShort();
+        post.glyphNameIndex = new Array(post.numberOfGlyphs);
+        for (i = 0; i < post.numberOfGlyphs; i++) {
+            post.glyphNameIndex[i] = p.parseUShort();
+        }
+
+        post.names = [];
+        for (i = 0; i < post.numberOfGlyphs; i++) {
+            if (post.glyphNameIndex[i] >= encoding.standardNames.length) {
+                var nameLength = p.parseChar();
+                post.names.push(p.parseString(nameLength));
+            }
+        }
+
+        break;
+    case 2.5:
+        post.numberOfGlyphs = p.parseUShort();
+        post.offset = new Array(post.numberOfGlyphs);
+        for (i = 0; i < post.numberOfGlyphs; i++) {
+            post.offset[i] = p.parseChar();
+        }
+
+        break;
+    }
+    return post;
+}
+
+function makePostTable() {
+    return new table.Table('post', [
+        {name: 'version', type: 'FIXED', value: 0x00030000},
+        {name: 'italicAngle', type: 'FIXED', value: 0},
+        {name: 'underlinePosition', type: 'FWORD', value: 0},
+        {name: 'underlineThickness', type: 'FWORD', value: 0},
+        {name: 'isFixedPitch', type: 'ULONG', value: 0},
+        {name: 'minMemType42', type: 'ULONG', value: 0},
+        {name: 'maxMemType42', type: 'ULONG', value: 0},
+        {name: 'minMemType1', type: 'ULONG', value: 0},
+        {name: 'maxMemType1', type: 'ULONG', value: 0}
+    ]);
+}
+
+exports.parse = parsePostTable;
+exports.make = makePostTable;
+
+},{"../encoding":4,"../parse":9,"../table":11}],25:[function(_dereq_,module,exports){
+// The `sfnt` wrapper provides organization for the tables in the font.
+// It is the top-level data structure in a font.
+// https://www.microsoft.com/typography/OTSPEC/otff.htm
+// Recommendations for creating OpenType Fonts:
+// http://www.microsoft.com/typography/otspec140/recom.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var table = _dereq_('../table');
+
+var cmap = _dereq_('./cmap');
+var cff = _dereq_('./cff');
+var head = _dereq_('./head');
+var hhea = _dereq_('./hhea');
+var hmtx = _dereq_('./hmtx');
+var maxp = _dereq_('./maxp');
+var _name = _dereq_('./name');
+var os2 = _dereq_('./os2');
+var post = _dereq_('./post');
+
+function log2(v) {
+    return Math.log(v) / Math.log(2) | 0;
+}
+
+function computeCheckSum(bytes) {
+    while (bytes.length % 4 !== 0) {
+        bytes.push(0);
+    }
+
+    var sum = 0;
+    for (var i = 0; i < bytes.length; i += 4) {
+        sum += (bytes[i] << 24) +
+            (bytes[i + 1] << 16) +
+            (bytes[i + 2] << 8) +
+            (bytes[i + 3]);
+    }
+
+    sum %= Math.pow(2, 32);
+    return sum;
+}
+
+function makeTableRecord(tag, checkSum, offset, length) {
+    return new table.Table('Table Record', [
+        {name: 'tag', type: 'TAG', value: tag !== undefined ? tag : ''},
+        {name: 'checkSum', type: 'ULONG', value: checkSum !== undefined ? checkSum : 0},
+        {name: 'offset', type: 'ULONG', value: offset !== undefined ? offset : 0},
+        {name: 'length', type: 'ULONG', value: length !== undefined ? length : 0}
+    ]);
+}
+
+function makeSfntTable(tables) {
+    var sfnt = new table.Table('sfnt', [
+        {name: 'version', type: 'TAG', value: 'OTTO'},
+        {name: 'numTables', type: 'USHORT', value: 0},
+        {name: 'searchRange', type: 'USHORT', value: 0},
+        {name: 'entrySelector', type: 'USHORT', value: 0},
+        {name: 'rangeShift', type: 'USHORT', value: 0}
+    ]);
+    sfnt.tables = tables;
+    sfnt.numTables = tables.length;
+    var highestPowerOf2 = Math.pow(2, log2(sfnt.numTables));
+    sfnt.searchRange = 16 * highestPowerOf2;
+    sfnt.entrySelector = log2(highestPowerOf2);
+    sfnt.rangeShift = sfnt.numTables * 16 - sfnt.searchRange;
+
+    var recordFields = [];
+    var tableFields = [];
+
+    var offset = sfnt.sizeOf() + (makeTableRecord().sizeOf() * sfnt.numTables);
+    while (offset % 4 !== 0) {
+        offset += 1;
+        tableFields.push({name: 'padding', type: 'BYTE', value: 0});
+    }
+
+    for (var i = 0; i < tables.length; i += 1) {
+        var t = tables[i];
+        check.argument(t.tableName.length === 4, 'Table name' + t.tableName + ' is invalid.');
+        var tableLength = t.sizeOf();
+        var tableRecord = makeTableRecord(t.tableName, computeCheckSum(t.encode()), offset, tableLength);
+        recordFields.push({name: tableRecord.tag + ' Table Record', type: 'TABLE', value: tableRecord});
+        tableFields.push({name: t.tableName + ' table', type: 'TABLE', value: t});
+        offset += tableLength;
+        check.argument(!isNaN(offset), 'Something went wrong calculating the offset.');
+        while (offset % 4 !== 0) {
+            offset += 1;
+            tableFields.push({name: 'padding', type: 'BYTE', value: 0});
+        }
+    }
+
+    // Table records need to be sorted alphabetically.
+    recordFields.sort(function(r1, r2) {
+        if (r1.value.tag > r2.value.tag) {
+            return 1;
+        } else {
+            return -1;
+        }
+    });
+
+    sfnt.fields = sfnt.fields.concat(recordFields);
+    sfnt.fields = sfnt.fields.concat(tableFields);
+    return sfnt;
+}
+
+// Get the metrics for a character. If the string has more than one character
+// this function returns metrics for the first available character.
+// You can provide optional fallback metrics if no characters are available.
+function metricsForChar(font, chars, notFoundMetrics) {
+    for (var i = 0; i < chars.length; i += 1) {
+        var glyphIndex = font.charToGlyphIndex(chars[i]);
+        if (glyphIndex > 0) {
+            var glyph = font.glyphs.get(glyphIndex);
+            return glyph.getMetrics();
+        }
+    }
+
+    return notFoundMetrics;
+}
+
+function average(vs) {
+    var sum = 0;
+    for (var i = 0; i < vs.length; i += 1) {
+        sum += vs[i];
+    }
+
+    return sum / vs.length;
+}
+
+// Convert the font object to a SFNT data structure.
+// This structure contains all the necessary tables and metadata to create a binary OTF file.
+function fontToSfntTable(font) {
+    var xMins = [];
+    var yMins = [];
+    var xMaxs = [];
+    var yMaxs = [];
+    var advanceWidths = [];
+    var leftSideBearings = [];
+    var rightSideBearings = [];
+    var firstCharIndex;
+    var lastCharIndex = 0;
+    var ulUnicodeRange1 = 0;
+    var ulUnicodeRange2 = 0;
+    var ulUnicodeRange3 = 0;
+    var ulUnicodeRange4 = 0;
+
+    for (var i = 0; i < font.glyphs.length; i += 1) {
+        var glyph = font.glyphs.get(i);
+        var unicode = glyph.unicode | 0;
+        if (firstCharIndex > unicode || firstCharIndex === null) {
+            firstCharIndex = unicode;
+        }
+
+        if (lastCharIndex < unicode) {
+            lastCharIndex = unicode;
+        }
+
+        var position = os2.getUnicodeRange(unicode);
+        if (position < 32) {
+            ulUnicodeRange1 |= 1 << position;
+        } else if (position < 64) {
+            ulUnicodeRange2 |= 1 << position - 32;
+        } else if (position < 96) {
+            ulUnicodeRange3 |= 1 << position - 64;
+        } else if (position < 123) {
+            ulUnicodeRange4 |= 1 << position - 96;
+        } else {
+            throw new Error('Unicode ranges bits > 123 are reserved for internal usage');
+        }
+        // Skip non-important characters.
+        if (glyph.name === '.notdef') continue;
+        var metrics = glyph.getMetrics();
+        xMins.push(metrics.xMin);
+        yMins.push(metrics.yMin);
+        xMaxs.push(metrics.xMax);
+        yMaxs.push(metrics.yMax);
+        leftSideBearings.push(metrics.leftSideBearing);
+        rightSideBearings.push(metrics.rightSideBearing);
+        advanceWidths.push(glyph.advanceWidth);
+    }
+
+    var globals = {
+        xMin: Math.min.apply(null, xMins),
+        yMin: Math.min.apply(null, yMins),
+        xMax: Math.max.apply(null, xMaxs),
+        yMax: Math.max.apply(null, yMaxs),
+        advanceWidthMax: Math.max.apply(null, advanceWidths),
+        advanceWidthAvg: average(advanceWidths),
+        minLeftSideBearing: Math.min.apply(null, leftSideBearings),
+        maxLeftSideBearing: Math.max.apply(null, leftSideBearings),
+        minRightSideBearing: Math.min.apply(null, rightSideBearings)
+    };
+    globals.ascender = font.ascender !== undefined ? font.ascender : globals.yMax;
+    globals.descender = font.descender !== undefined ? font.descender : globals.yMin;
+
+    var headTable = head.make({
+        unitsPerEm: font.unitsPerEm,
+        xMin: globals.xMin,
+        yMin: globals.yMin,
+        xMax: globals.xMax,
+        yMax: globals.yMax
+    });
+
+    var hheaTable = hhea.make({
+        ascender: globals.ascender,
+        descender: globals.descender,
+        advanceWidthMax: globals.advanceWidthMax,
+        minLeftSideBearing: globals.minLeftSideBearing,
+        minRightSideBearing: globals.minRightSideBearing,
+        xMaxExtent: globals.maxLeftSideBearing + (globals.xMax - globals.xMin),
+        numberOfHMetrics: font.glyphs.length
+    });
+
+    var maxpTable = maxp.make(font.glyphs.length);
+
+    var os2Table = os2.make({
+        xAvgCharWidth: Math.round(globals.advanceWidthAvg),
+        usWeightClass: 500, // Medium FIXME Make this configurable
+        usWidthClass: 5, // Medium (normal) FIXME Make this configurable
+        usFirstCharIndex: firstCharIndex,
+        usLastCharIndex: lastCharIndex,
+        ulUnicodeRange1: ulUnicodeRange1,
+        ulUnicodeRange2: ulUnicodeRange2,
+        ulUnicodeRange3: ulUnicodeRange3,
+        ulUnicodeRange4: ulUnicodeRange4,
+        // See http://typophile.com/node/13081 for more info on vertical metrics.
+        // We get metrics for typical characters (such as "x" for xHeight).
+        // We provide some fallback characters if characters are unavailable: their
+        // ordering was chosen experimentally.
+        sTypoAscender: globals.ascender,
+        sTypoDescender: globals.descender,
+        sTypoLineGap: 0,
+        usWinAscent: globals.ascender,
+        usWinDescent: -globals.descender,
+        sxHeight: metricsForChar(font, 'xyvw', {yMax: 0}).yMax,
+        sCapHeight: metricsForChar(font, 'HIKLEFJMNTZBDPRAGOQSUVWXY', globals).yMax,
+        usBreakChar: font.hasChar(' ') ? 32 : 0 // Use space as the break character, if available.
+    });
+
+    var hmtxTable = hmtx.make(font.glyphs);
+    var cmapTable = cmap.make(font.glyphs);
+
+    var fullName = font.familyName + ' ' + font.styleName;
+    var postScriptName = font.familyName.replace(/\s/g, '') + '-' + font.styleName;
+    var nameTable = _name.make({
+        copyright: font.copyright,
+        fontFamily: font.familyName,
+        fontSubfamily: font.styleName,
+        uniqueID: font.manufacturer + ':' + fullName,
+        fullName: fullName,
+        version: font.version,
+        postScriptName: postScriptName,
+        trademark: font.trademark,
+        manufacturer: font.manufacturer,
+        designer: font.designer,
+        description: font.description,
+        manufacturerURL: font.manufacturerURL,
+        designerURL: font.designerURL,
+        license: font.license,
+        licenseURL: font.licenseURL,
+        preferredFamily: font.familyName,
+        preferredSubfamily: font.styleName
+    });
+    var postTable = post.make();
+    var cffTable = cff.make(font.glyphs, {
+        version: font.version,
+        fullName: fullName,
+        familyName: font.familyName,
+        weightName: font.styleName,
+        postScriptName: postScriptName,
+        unitsPerEm: font.unitsPerEm
+    });
+    // Order the tables according to the the OpenType specification 1.4.
+    var tables = [headTable, hheaTable, maxpTable, os2Table, nameTable, cmapTable, postTable, cffTable, hmtxTable];
+
+    var sfntTable = makeSfntTable(tables);
+
+    // Compute the font's checkSum and store it in head.checkSumAdjustment.
+    var bytes = sfntTable.encode();
+    var checkSum = computeCheckSum(bytes);
+    var tableFields = sfntTable.fields;
+    var checkSumAdjusted = false;
+    for (i = 0; i < tableFields.length; i += 1) {
+        if (tableFields[i].name === 'head table') {
+            tableFields[i].value.checkSumAdjustment = 0xB1B0AFBA - checkSum;
+            checkSumAdjusted = true;
+            break;
+        }
+    }
+
+    if (!checkSumAdjusted) {
+        throw new Error('Could not find head table with checkSum to adjust.');
+    }
+
+    return sfntTable;
+}
+
+exports.computeCheckSum = computeCheckSum;
+exports.make = makeSfntTable;
+exports.fontToTable = fontToSfntTable;
+
+},{"../check":2,"../table":11,"./cff":12,"./cmap":13,"./head":16,"./hhea":17,"./hmtx":18,"./maxp":21,"./name":22,"./os2":23,"./post":24}],26:[function(_dereq_,module,exports){
+// Data types used in the OpenType font file.
+// All OpenType fonts use Motorola-style byte ordering (Big Endian)
+
+/* global WeakMap */
+
+'use strict';
+
+var check = _dereq_('./check');
+
+var LIMIT16 = 32768; // The limit at which a 16-bit number switches signs == 2^15
+var LIMIT32 = 2147483648; // The limit at which a 32-bit number switches signs == 2 ^ 31
+
+var decode = {};
+var encode = {};
+var sizeOf = {};
+
+// Return a function that always returns the same value.
+function constant(v) {
+    return function() {
+        return v;
+    };
+}
+
+// OpenType data types //////////////////////////////////////////////////////
+
+// Convert an 8-bit unsigned integer to a list of 1 byte.
+encode.BYTE = function(v) {
+    check.argument(v >= 0 && v <= 255, 'Byte value should be between 0 and 255.');
+    return [v];
+};
+
+sizeOf.BYTE = constant(1);
+
+// Convert a 8-bit signed integer to a list of 1 byte.
+encode.CHAR = function(v) {
+    return [v.charCodeAt(0)];
+};
+
+sizeOf.BYTE = constant(1);
+
+// Convert an ASCII string to a list of bytes.
+encode.CHARARRAY = function(v) {
+    var b = [];
+    for (var i = 0; i < v.length; i += 1) {
+        b.push(v.charCodeAt(i));
+    }
+
+    return b;
+};
+
+sizeOf.CHARARRAY = function(v) {
+    return v.length;
+};
+
+// Convert a 16-bit unsigned integer to a list of 2 bytes.
+encode.USHORT = function(v) {
+    return [(v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.USHORT = constant(2);
+
+// Convert a 16-bit signed integer to a list of 2 bytes.
+encode.SHORT = function(v) {
+    // Two's complement
+    if (v >= LIMIT16) {
+        v = -(2 * LIMIT16 - v);
+    }
+
+    return [(v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.SHORT = constant(2);
+
+// Convert a 24-bit unsigned integer to a list of 3 bytes.
+encode.UINT24 = function(v) {
+    return [(v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.UINT24 = constant(3);
+
+// Convert a 32-bit unsigned integer to a list of 4 bytes.
+encode.ULONG = function(v) {
+    return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.ULONG = constant(4);
+
+// Convert a 32-bit unsigned integer to a list of 4 bytes.
+encode.LONG = function(v) {
+    // Two's complement
+    if (v >= LIMIT32) {
+        v = -(2 * LIMIT32 - v);
+    }
+
+    return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.LONG = constant(4);
+
+encode.FIXED = encode.ULONG;
+sizeOf.FIXED = sizeOf.ULONG;
+
+encode.FWORD = encode.SHORT;
+sizeOf.FWORD = sizeOf.SHORT;
+
+encode.UFWORD = encode.USHORT;
+sizeOf.UFWORD = sizeOf.USHORT;
+
+// FIXME Implement LONGDATETIME
+encode.LONGDATETIME = function() {
+    return [0, 0, 0, 0, 0, 0, 0, 0];
+};
+
+sizeOf.LONGDATETIME = constant(8);
+
+// Convert a 4-char tag to a list of 4 bytes.
+encode.TAG = function(v) {
+    check.argument(v.length === 4, 'Tag should be exactly 4 ASCII characters.');
+    return [v.charCodeAt(0),
+            v.charCodeAt(1),
+            v.charCodeAt(2),
+            v.charCodeAt(3)];
+};
+
+sizeOf.TAG = constant(4);
+
+// CFF data types ///////////////////////////////////////////////////////////
+
+encode.Card8 = encode.BYTE;
+sizeOf.Card8 = sizeOf.BYTE;
+
+encode.Card16 = encode.USHORT;
+sizeOf.Card16 = sizeOf.USHORT;
+
+encode.OffSize = encode.BYTE;
+sizeOf.OffSize = sizeOf.BYTE;
+
+encode.SID = encode.USHORT;
+sizeOf.SID = sizeOf.USHORT;
+
+// Convert a numeric operand or charstring number to a variable-size list of bytes.
+encode.NUMBER = function(v) {
+    if (v >= -107 && v <= 107) {
+        return [v + 139];
+    } else if (v >= 108 && v <= 1131) {
+        v = v - 108;
+        return [(v >> 8) + 247, v & 0xFF];
+    } else if (v >= -1131 && v <= -108) {
+        v = -v - 108;
+        return [(v >> 8) + 251, v & 0xFF];
+    } else if (v >= -32768 && v <= 32767) {
+        return encode.NUMBER16(v);
+    } else {
+        return encode.NUMBER32(v);
+    }
+};
+
+sizeOf.NUMBER = function(v) {
+    return encode.NUMBER(v).length;
+};
+
+// Convert a signed number between -32768 and +32767 to a three-byte value.
+// This ensures we always use three bytes, but is not the most compact format.
+encode.NUMBER16 = function(v) {
+    return [28, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.NUMBER16 = constant(2);
+
+// Convert a signed number between -(2^31) and +(2^31-1) to a four-byte value.
+// This is useful if you want to be sure you always use four bytes,
+// at the expense of wasting a few bytes for smaller numbers.
+encode.NUMBER32 = function(v) {
+    return [29, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.NUMBER32 = constant(4);
+
+encode.REAL = function(v) {
+    var value = v.toString();
+
+    // Some numbers use an epsilon to encode the value. (e.g. JavaScript will store 0.0000001 as 1e-7)
+    // This code converts it back to a number without the epsilon.
+    var m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value);
+    if (m) {
+        var epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length));
+        value = (Math.round(v * epsilon) / epsilon).toString();
+    }
+
+    var nibbles = '';
+    var i;
+    var ii;
+    for (i = 0, ii = value.length; i < ii; i += 1) {
+        var c = value[i];
+        if (c === 'e') {
+            nibbles += value[++i] === '-' ? 'c' : 'b';
+        } else if (c === '.') {
+            nibbles += 'a';
+        } else if (c === '-') {
+            nibbles += 'e';
+        } else {
+            nibbles += c;
+        }
+    }
+
+    nibbles += (nibbles.length & 1) ? 'f' : 'ff';
+    var out = [30];
+    for (i = 0, ii = nibbles.length; i < ii; i += 2) {
+        out.push(parseInt(nibbles.substr(i, 2), 16));
+    }
+
+    return out;
+};
+
+sizeOf.REAL = function(v) {
+    return encode.REAL(v).length;
+};
+
+encode.NAME = encode.CHARARRAY;
+sizeOf.NAME = sizeOf.CHARARRAY;
+
+encode.STRING = encode.CHARARRAY;
+sizeOf.STRING = sizeOf.CHARARRAY;
+
+// Convert a ASCII string to a list of UTF16 bytes.
+encode.UTF16 = function(v) {
+    var b = [];
+    for (var i = 0; i < v.length; i += 1) {
+        b.push(0);
+        b.push(v.charCodeAt(i));
+    }
+
+    return b;
+};
+
+sizeOf.UTF16 = function(v) {
+    return v.length * 2;
+};
+
+// Convert a list of values to a CFF INDEX structure.
+// The values should be objects containing name / type / value.
+encode.INDEX = function(l) {
+    var i;
+    //var offset, offsets, offsetEncoder, encodedOffsets, encodedOffset, data,
+    //    dataSize, i, v;
+    // Because we have to know which data type to use to encode the offsets,
+    // we have to go through the values twice: once to encode the data and
+    // calculate the offets, then again to encode the offsets using the fitting data type.
+    var offset = 1; // First offset is always 1.
+    var offsets = [offset];
+    var data = [];
+    var dataSize = 0;
+    for (i = 0; i < l.length; i += 1) {
+        var v = encode.OBJECT(l[i]);
+        Array.prototype.push.apply(data, v);
+        dataSize += v.length;
+        offset += v.length;
+        offsets.push(offset);
+    }
+
+    if (data.length === 0) {
+        return [0, 0];
+    }
+
+    var encodedOffsets = [];
+    var offSize = (1 + Math.floor(Math.log(dataSize) / Math.log(2)) / 8) | 0;
+    var offsetEncoder = [undefined, encode.BYTE, encode.USHORT, encode.UINT24, encode.ULONG][offSize];
+    for (i = 0; i < offsets.length; i += 1) {
+        var encodedOffset = offsetEncoder(offsets[i]);
+        Array.prototype.push.apply(encodedOffsets, encodedOffset);
+    }
+
+    return Array.prototype.concat(encode.Card16(l.length),
+                           encode.OffSize(offSize),
+                           encodedOffsets,
+                           data);
+};
+
+sizeOf.INDEX = function(v) {
+    return encode.INDEX(v).length;
+};
+
+// Convert an object to a CFF DICT structure.
+// The keys should be numeric.
+// The values should be objects containing name / type / value.
+encode.DICT = function(m) {
+    var d = [];
+    var keys = Object.keys(m);
+    var length = keys.length;
+
+    for (var i = 0; i < length; i += 1) {
+        // Object.keys() return string keys, but our keys are always numeric.
+        var k = parseInt(keys[i], 0);
+        var v = m[k];
+        // Value comes before the key.
+        d = d.concat(encode.OPERAND(v.value, v.type));
+        d = d.concat(encode.OPERATOR(k));
+    }
+
+    return d;
+};
+
+sizeOf.DICT = function(m) {
+    return encode.DICT(m).length;
+};
+
+encode.OPERATOR = function(v) {
+    if (v < 1200) {
+        return [v];
+    } else {
+        return [12, v - 1200];
+    }
+};
+
+encode.OPERAND = function(v, type) {
+    var d = [];
+    if (Array.isArray(type)) {
+        for (var i = 0; i < type.length; i += 1) {
+            check.argument(v.length === type.length, 'Not enough arguments given for type' + type);
+            d = d.concat(encode.OPERAND(v[i], type[i]));
+        }
+    } else {
+        if (type === 'SID') {
+            d = d.concat(encode.NUMBER(v));
+        } else if (type === 'offset') {
+            // We make it easy for ourselves and always encode offsets as
+            // 4 bytes. This makes offset calculation for the top dict easier.
+            d = d.concat(encode.NUMBER32(v));
+        } else if (type === 'number') {
+            d = d.concat(encode.NUMBER(v));
+        } else if (type === 'real') {
+            d = d.concat(encode.REAL(v));
+        } else {
+            throw new Error('Unknown operand type ' + type);
+            // FIXME Add support for booleans
+        }
+    }
+
+    return d;
+};
+
+encode.OP = encode.BYTE;
+sizeOf.OP = sizeOf.BYTE;
+
+// memoize charstring encoding using WeakMap if available
+var wmm = typeof WeakMap === 'function' && new WeakMap();
+// Convert a list of CharString operations to bytes.
+encode.CHARSTRING = function(ops) {
+    if (wmm && wmm.has(ops)) {
+        return wmm.get(ops);
+    }
+
+    var d = [];
+    var length = ops.length;
+
+    for (var i = 0; i < length; i += 1) {
+        var op = ops[i];
+        d = d.concat(encode[op.type](op.value));
+    }
+
+    if (wmm) {
+        wmm.set(ops, d);
+    }
+
+    return d;
+};
+
+sizeOf.CHARSTRING = function(ops) {
+    return encode.CHARSTRING(ops).length;
+};
+
+// Utility functions ////////////////////////////////////////////////////////
+
+// Convert an object containing name / type / value to bytes.
+encode.OBJECT = function(v) {
+    var encodingFunction = encode[v.type];
+    check.argument(encodingFunction !== undefined, 'No encoding function for type ' + v.type);
+    return encodingFunction(v.value);
+};
+
+// Convert a table object to bytes.
+// A table contains a list of fields containing the metadata (name, type and default value).
+// The table itself has the field values set as attributes.
+encode.TABLE = function(table) {
+    var d = [];
+    var length = table.fields.length;
+
+    for (var i = 0; i < length; i += 1) {
+        var field = table.fields[i];
+        var encodingFunction = encode[field.type];
+        check.argument(encodingFunction !== undefined, 'No encoding function for field type ' + field.type);
+        var value = table[field.name];
+        if (value === undefined) {
+            value = field.value;
+        }
+
+        var bytes = encodingFunction(value);
+        d = d.concat(bytes);
+    }
+
+    return d;
+};
+
+// Merge in a list of bytes.
+encode.LITERAL = function(v) {
+    return v;
+};
+
+sizeOf.LITERAL = function(v) {
+    return v.length;
+};
+
+exports.decode = decode;
+exports.encode = encode;
+exports.sizeOf = sizeOf;
+
+},{"./check":2}],27:[function(_dereq_,module,exports){
+/*!
+  * Reqwest! A general purpose XHR connection manager
+  * license MIT (c) Dustin Diaz 2014
+  * https://github.com/ded/reqwest
+  */
+
+!function (name, context, definition) {
+  if (typeof module != 'undefined' && module.exports) module.exports = definition()
+  else if (typeof define == 'function' && define.amd) define(definition)
+  else context[name] = definition()
+}('reqwest', this, function () {
+
+  var win = window
+    , doc = document
+    , httpsRe = /^http/
+    , protocolRe = /(^\w+):\/\//
+    , twoHundo = /^(20\d|1223)$/ //http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
+    , byTag = 'getElementsByTagName'
+    , readyState = 'readyState'
+    , contentType = 'Content-Type'
+    , requestedWith = 'X-Requested-With'
+    , head = doc[byTag]('head')[0]
+    , uniqid = 0
+    , callbackPrefix = 'reqwest_' + (+new Date())
+    , lastValue // data stored by the most recent JSONP callback
+    , xmlHttpRequest = 'XMLHttpRequest'
+    , xDomainRequest = 'XDomainRequest'
+    , noop = function () {}
+
+    , isArray = typeof Array.isArray == 'function'
+        ? Array.isArray
+        : function (a) {
+            return a instanceof Array
+          }
+
+    , defaultHeaders = {
+          'contentType': 'application/x-www-form-urlencoded'
+        , 'requestedWith': xmlHttpRequest
+        , 'accept': {
+              '*':  'text/javascript, text/html, application/xml, text/xml, */*'
+            , 'xml':  'application/xml, text/xml'
+            , 'html': 'text/html'
+            , 'text': 'text/plain'
+            , 'json': 'application/json, text/javascript'
+            , 'js':   'application/javascript, text/javascript'
+          }
+      }
+
+    , xhr = function(o) {
+        // is it x-domain
+        if (o['crossOrigin'] === true) {
+          var xhr = win[xmlHttpRequest] ? new XMLHttpRequest() : null
+          if (xhr && 'withCredentials' in xhr) {
+            return xhr
+          } else if (win[xDomainRequest]) {
+            return new XDomainRequest()
+          } else {
+            throw new Error('Browser does not support cross-origin requests')
+          }
+        } else if (win[xmlHttpRequest]) {
+          return new XMLHttpRequest()
+        } else {
+          return new ActiveXObject('Microsoft.XMLHTTP')
+        }
+      }
+    , globalSetupOptions = {
+        dataFilter: function (data) {
+          return data
+        }
+      }
+
+  function succeed(r) {
+    var protocol = protocolRe.exec(r.url);
+    protocol = (protocol && protocol[1]) || window.location.protocol;
+    return httpsRe.test(protocol) ? twoHundo.test(r.request.status) : !!r.request.response;
+  }
+
+  function handleReadyState(r, success, error) {
+    return function () {
+      // use _aborted to mitigate against IE err c00c023f
+      // (can't read props on aborted request objects)
+      if (r._aborted) return error(r.request)
+      if (r._timedOut) return error(r.request, 'Request is aborted: timeout')
+      if (r.request && r.request[readyState] == 4) {
+        r.request.onreadystatechange = noop
+        if (succeed(r)) success(r.request)
+        else
+          error(r.request)
+      }
+    }
+  }
+
+  function setHeaders(http, o) {
+    var headers = o['headers'] || {}
+      , h
+
+    headers['Accept'] = headers['Accept']
+      || defaultHeaders['accept'][o['type']]
+      || defaultHeaders['accept']['*']
+
+    var isAFormData = typeof FormData === 'function' && (o['data'] instanceof FormData);
+    // breaks cross-origin requests with legacy browsers
+    if (!o['crossOrigin'] && !headers[requestedWith]) headers[requestedWith] = defaultHeaders['requestedWith']
+    if (!headers[contentType] && !isAFormData) headers[contentType] = o['contentType'] || defaultHeaders['contentType']
+    for (h in headers)
+      headers.hasOwnProperty(h) && 'setRequestHeader' in http && http.setRequestHeader(h, headers[h])
+  }
+
+  function setCredentials(http, o) {
+    if (typeof o['withCredentials'] !== 'undefined' && typeof http.withCredentials !== 'undefined') {
+      http.withCredentials = !!o['withCredentials']
+    }
+  }
+
+  function generalCallback(data) {
+    lastValue = data
+  }
+
+  function urlappend (url, s) {
+    return url + (/\?/.test(url) ? '&' : '?') + s
+  }
+
+  function handleJsonp(o, fn, err, url) {
+    var reqId = uniqid++
+      , cbkey = o['jsonpCallback'] || 'callback' // the 'callback' key
+      , cbval = o['jsonpCallbackName'] || reqwest.getcallbackPrefix(reqId)
+      , cbreg = new RegExp('((^|\\?|&)' + cbkey + ')=([^&]+)')
+      , match = url.match(cbreg)
+      , script = doc.createElement('script')
+      , loaded = 0
+      , isIE10 = navigator.userAgent.indexOf('MSIE 10.0') !== -1
+
+    if (match) {
+      if (match[3] === '?') {
+        url = url.replace(cbreg, '$1=' + cbval) // wildcard callback func name
+      } else {
+        cbval = match[3] // provided callback func name
+      }
+    } else {
+      url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em
+    }
+
+    win[cbval] = generalCallback
+
+    script.type = 'text/javascript'
+    script.src = url
+    script.async = true
+    if (typeof script.onreadystatechange !== 'undefined' && !isIE10) {
+      // need this for IE due to out-of-order onreadystatechange(), binding script
+      // execution to an event listener gives us control over when the script
+      // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
+      script.htmlFor = script.id = '_reqwest_' + reqId
+    }
+
+    script.onload = script.onreadystatechange = function () {
+      if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) {
+        return false
+      }
+      script.onload = script.onreadystatechange = null
+      script.onclick && script.onclick()
+      // Call the user callback with the last value stored and clean up values and scripts.
+      fn(lastValue)
+      lastValue = undefined
+      head.removeChild(script)
+      loaded = 1
+    }
+
+    // Add the script to the DOM head
+    head.appendChild(script)
+
+    // Enable JSONP timeout
+    return {
+      abort: function () {
+        script.onload = script.onreadystatechange = null
+        err({}, 'Request is aborted: timeout', {})
+        lastValue = undefined
+        head.removeChild(script)
+        loaded = 1
+      }
+    }
+  }
+
+  function getRequest(fn, err) {
+    var o = this.o
+      , method = (o['method'] || 'GET').toUpperCase()
+      , url = typeof o === 'string' ? o : o['url']
+      // convert non-string objects to query-string form unless o['processData'] is false
+      , data = (o['processData'] !== false && o['data'] && typeof o['data'] !== 'string')
+        ? reqwest.toQueryString(o['data'])
+        : (o['data'] || null)
+      , http
+      , sendWait = false
+
+    // if we're working on a GET request and we have data then we should append
+    // query string to end of URL and not post data
+    if ((o['type'] == 'jsonp' || method == 'GET') && data) {
+      url = urlappend(url, data)
+      data = null
+    }
+
+    if (o['type'] == 'jsonp') return handleJsonp(o, fn, err, url)
+
+    // get the xhr from the factory if passed
+    // if the factory returns null, fall-back to ours
+    http = (o.xhr && o.xhr(o)) || xhr(o)
+
+    http.open(method, url, o['async'] === false ? false : true)
+    setHeaders(http, o)
+    setCredentials(http, o)
+    if (win[xDomainRequest] && http instanceof win[xDomainRequest]) {
+        http.onload = fn
+        http.onerror = err
+        // NOTE: see
+        // http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/30ef3add-767c-4436-b8a9-f1ca19b4812e
+        http.onprogress = function() {}
+        sendWait = true
+    } else {
+      http.onreadystatechange = handleReadyState(this, fn, err)
+    }
+    o['before'] && o['before'](http)
+    if (sendWait) {
+      setTimeout(function () {
+        http.send(data)
+      }, 200)
+    } else {
+      http.send(data)
+    }
+    return http
+  }
+
+  function Reqwest(o, fn) {
+    this.o = o
+    this.fn = fn
+
+    init.apply(this, arguments)
+  }
+
+  function setType(header) {
+    // json, javascript, text/plain, text/html, xml
+    if (header.match('json')) return 'json'
+    if (header.match('javascript')) return 'js'
+    if (header.match('text')) return 'html'
+    if (header.match('xml')) return 'xml'
+  }
+
+  function init(o, fn) {
+
+    this.url = typeof o == 'string' ? o : o['url']
+    this.timeout = null
+
+    // whether request has been fulfilled for purpose
+    // of tracking the Promises
+    this._fulfilled = false
+    // success handlers
+    this._successHandler = function(){}
+    this._fulfillmentHandlers = []
+    // error handlers
+    this._errorHandlers = []
+    // complete (both success and fail) handlers
+    this._completeHandlers = []
+    this._erred = false
+    this._responseArgs = {}
+
+    var self = this
+
+    fn = fn || function () {}
+
+    if (o['timeout']) {
+      this.timeout = setTimeout(function () {
+        timedOut()
+      }, o['timeout'])
+    }
+
+    if (o['success']) {
+      this._successHandler = function () {
+        o['success'].apply(o, arguments)
+      }
+    }
+
+    if (o['error']) {
+      this._errorHandlers.push(function () {
+        o['error'].apply(o, arguments)
+      })
+    }
+
+    if (o['complete']) {
+      this._completeHandlers.push(function () {
+        o['complete'].apply(o, arguments)
+      })
+    }
+
+    function complete (resp) {
+      o['timeout'] && clearTimeout(self.timeout)
+      self.timeout = null
+      while (self._completeHandlers.length > 0) {
+        self._completeHandlers.shift()(resp)
+      }
+    }
+
+    function success (resp) {
+      var type = o['type'] || resp && setType(resp.getResponseHeader('Content-Type')) // resp can be undefined in IE
+      resp = (type !== 'jsonp') ? self.request : resp
+      // use global data filter on response text
+      var filteredResponse = globalSetupOptions.dataFilter(resp.responseText, type)
+        , r = filteredResponse
+      try {
+        resp.responseText = r
+      } catch (e) {
+        // can't assign this in IE<=8, just ignore
+      }
+      if (r) {
+        switch (type) {
+        case 'json':
+          try {
+            resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')')
+          } catch (err) {
+            return error(resp, 'Could not parse JSON in response', err)
+          }
+          break
+        case 'js':
+          resp = eval(r)
+          break
+        case 'html':
+          resp = r
+          break
+        case 'xml':
+          resp = resp.responseXML
+              && resp.responseXML.parseError // IE trololo
+              && resp.responseXML.parseError.errorCode
+              && resp.responseXML.parseError.reason
+            ? null
+            : resp.responseXML
+          break
+        }
+      }
+
+      self._responseArgs.resp = resp
+      self._fulfilled = true
+      fn(resp)
+      self._successHandler(resp)
+      while (self._fulfillmentHandlers.length > 0) {
+        resp = self._fulfillmentHandlers.shift()(resp)
+      }
+
+      complete(resp)
+    }
+
+    function timedOut() {
+      self._timedOut = true
+      self.request.abort()      
+    }
+
+    function error(resp, msg, t) {
+      resp = self.request
+      self._responseArgs.resp = resp
+      self._responseArgs.msg = msg
+      self._responseArgs.t = t
+      self._erred = true
+      while (self._errorHandlers.length > 0) {
+        self._errorHandlers.shift()(resp, msg, t)
+      }
+      complete(resp)
+    }
+
+    this.request = getRequest.call(this, success, error)
+  }
+
+  Reqwest.prototype = {
+    abort: function () {
+      this._aborted = true
+      this.request.abort()
+    }
+
+  , retry: function () {
+      init.call(this, this.o, this.fn)
+    }
+
+    /**
+     * Small deviation from the Promises A CommonJs specification
+     * http://wiki.commonjs.org/wiki/Promises/A
+     */
+
+    /**
+     * `then` will execute upon successful requests
+     */
+  , then: function (success, fail) {
+      success = success || function () {}
+      fail = fail || function () {}
+      if (this._fulfilled) {
+        this._responseArgs.resp = success(this._responseArgs.resp)
+      } else if (this._erred) {
+        fail(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t)
+      } else {
+        this._fulfillmentHandlers.push(success)
+        this._errorHandlers.push(fail)
+      }
+      return this
+    }
+
+    /**
+     * `always` will execute whether the request succeeds or fails
+     */
+  , always: function (fn) {
+      if (this._fulfilled || this._erred) {
+        fn(this._responseArgs.resp)
+      } else {
+        this._completeHandlers.push(fn)
+      }
+      return this
+    }
+
+    /**
+     * `fail` will execute when the request fails
+     */
+  , fail: function (fn) {
+      if (this._erred) {
+        fn(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t)
+      } else {
+        this._errorHandlers.push(fn)
+      }
+      return this
+    }
+  , 'catch': function (fn) {
+      return this.fail(fn)
+    }
+  }
+
+  function reqwest(o, fn) {
+    return new Reqwest(o, fn)
+  }
+
+  // normalize newline variants according to spec -> CRLF
+  function normalize(s) {
+    return s ? s.replace(/\r?\n/g, '\r\n') : ''
+  }
+
+  function serial(el, cb) {
+    var n = el.name
+      , t = el.tagName.toLowerCase()
+      , optCb = function (o) {
+          // IE gives value="" even where there is no value attribute
+          // 'specified' ref: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-862529273
+          if (o && !o['disabled'])
+            cb(n, normalize(o['attributes']['value'] && o['attributes']['value']['specified'] ? o['value'] : o['text']))
+        }
+      , ch, ra, val, i
+
+    // don't serialize elements that are disabled or without a name
+    if (el.disabled || !n) return
+
+    switch (t) {
+    case 'input':
+      if (!/reset|button|image|file/i.test(el.type)) {
+        ch = /checkbox/i.test(el.type)
+        ra = /radio/i.test(el.type)
+        val = el.value
+        // WebKit gives us "" instead of "on" if a checkbox has no value, so correct it here
+        ;(!(ch || ra) || el.checked) && cb(n, normalize(ch && val === '' ? 'on' : val))
+      }
+      break
+    case 'textarea':
+      cb(n, normalize(el.value))
+      break
+    case 'select':
+      if (el.type.toLowerCase() === 'select-one') {
+        optCb(el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null)
+      } else {
+        for (i = 0; el.length && i < el.length; i++) {
+          el.options[i].selected && optCb(el.options[i])
+        }
+      }
+      break
+    }
+  }
+
+  // collect up all form elements found from the passed argument elements all
+  // the way down to child elements; pass a '<form>' or form fields.
+  // called with 'this'=callback to use for serial() on each element
+  function eachFormElement() {
+    var cb = this
+      , e, i
+      , serializeSubtags = function (e, tags) {
+          var i, j, fa
+          for (i = 0; i < tags.length; i++) {
+            fa = e[byTag](tags[i])
+            for (j = 0; j < fa.length; j++) serial(fa[j], cb)
+          }
+        }
+
+    for (i = 0; i < arguments.length; i++) {
+      e = arguments[i]
+      if (/input|select|textarea/i.test(e.tagName)) serial(e, cb)
+      serializeSubtags(e, [ 'input', 'select', 'textarea' ])
+    }
+  }
+
+  // standard query string style serialization
+  function serializeQueryString() {
+    return reqwest.toQueryString(reqwest.serializeArray.apply(null, arguments))
+  }
+
+  // { 'name': 'value', ... } style serialization
+  function serializeHash() {
+    var hash = {}
+    eachFormElement.apply(function (name, value) {
+      if (name in hash) {
+        hash[name] && !isArray(hash[name]) && (hash[name] = [hash[name]])
+        hash[name].push(value)
+      } else hash[name] = value
+    }, arguments)
+    return hash
+  }
+
+  // [ { name: 'name', value: 'value' }, ... ] style serialization
+  reqwest.serializeArray = function () {
+    var arr = []
+    eachFormElement.apply(function (name, value) {
+      arr.push({name: name, value: value})
+    }, arguments)
+    return arr
+  }
+
+  reqwest.serialize = function () {
+    if (arguments.length === 0) return ''
+    var opt, fn
+      , args = Array.prototype.slice.call(arguments, 0)
+
+    opt = args.pop()
+    opt && opt.nodeType && args.push(opt) && (opt = null)
+    opt && (opt = opt.type)
+
+    if (opt == 'map') fn = serializeHash
+    else if (opt == 'array') fn = reqwest.serializeArray
+    else fn = serializeQueryString
+
+    return fn.apply(null, args)
+  }
+
+  reqwest.toQueryString = function (o, trad) {
+    var prefix, i
+      , traditional = trad || false
+      , s = []
+      , enc = encodeURIComponent
+      , add = function (key, value) {
+          // If value is a function, invoke it and return its value
+          value = ('function' === typeof value) ? value() : (value == null ? '' : value)
+          s[s.length] = enc(key) + '=' + enc(value)
+        }
+    // If an array was passed in, assume that it is an array of form elements.
+    if (isArray(o)) {
+      for (i = 0; o && i < o.length; i++) add(o[i]['name'], o[i]['value'])
+    } else {
+      // If traditional, encode the "old" way (the way 1.3.2 or older
+      // did it), otherwise encode params recursively.
+      for (prefix in o) {
+        if (o.hasOwnProperty(prefix)) buildParams(prefix, o[prefix], traditional, add)
+      }
+    }
+
+    // spaces should be + according to spec
+    return s.join('&').replace(/%20/g, '+')
+  }
+
+  function buildParams(prefix, obj, traditional, add) {
+    var name, i, v
+      , rbracket = /\[\]$/
+
+    if (isArray(obj)) {
+      // Serialize array item.
+      for (i = 0; obj && i < obj.length; i++) {
+        v = obj[i]
+        if (traditional || rbracket.test(prefix)) {
+          // Treat each array item as a scalar.
+          add(prefix, v)
+        } else {
+          buildParams(prefix + '[' + (typeof v === 'object' ? i : '') + ']', v, traditional, add)
+        }
+      }
+    } else if (obj && obj.toString() === '[object Object]') {
+      // Serialize object item.
+      for (name in obj) {
+        buildParams(prefix + '[' + name + ']', obj[name], traditional, add)
+      }
+
+    } else {
+      // Serialize scalar item.
+      add(prefix, obj)
+    }
+  }
+
+  reqwest.getcallbackPrefix = function () {
+    return callbackPrefix
+  }
+
+  // jQuery and Zepto compatibility, differences can be remapped here so you can call
+  // .ajax.compat(options, callback)
+  reqwest.compat = function (o, fn) {
+    if (o) {
+      o['type'] && (o['method'] = o['type']) && delete o['type']
+      o['dataType'] && (o['type'] = o['dataType'])
+      o['jsonpCallback'] && (o['jsonpCallbackName'] = o['jsonpCallback']) && delete o['jsonpCallback']
+      o['jsonp'] && (o['jsonpCallback'] = o['jsonp'])
+    }
+    return new Reqwest(o, fn)
+  }
+
+  reqwest.ajaxSetup = function (options) {
+    options = options || {}
+    for (var k in options) {
+      globalSetupOptions[k] = options[k]
+    }
+  }
+
+  return reqwest
+});
+
+},{}],28:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule 3D Primitives
+ * @for p5
+ * @requires core
+ * @requires p5.Geometry3D
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+_dereq_('./p5.Geometry3D');
+
+/**
+ * Draw a plane with given a width and height
+ * @method plane
+ * @param  {Number} width      width of the plane
+ * @param  {Number} height     height of the plane
+ * @return {p5}                the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //draw a plane with width 200 and height 200
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   plane(200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.plane = function(width, height){
+
+  width = width || 50;
+  height = height || 50;
+
+  //details for plane are highly optional
+  var detailX = typeof arguments[2] === Number ? arguments[2] : 1;
+  var detailY = typeof arguments[3] === Number ? arguments[3] : 1;
+
+  var gId = 'plane|'+width+'|'+height+'|'+detailX+'|'+detailY;
+
+  if(!this._renderer.geometryInHash(gId)){
+
+    var geometry3d = new p5.Geometry3D();
+
+    var createPlane = function(u, v){
+      var x = 2 * width * u - width;
+      var y = 2 * height * v - height;
+      var z = 0;
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d.parametricGeometry(createPlane, detailX, detailY);
+
+    var obj = geometry3d.generateObj();
+
+    this._renderer.initBuffer(gId, [obj]);
+
+  }
+
+  this._renderer.drawBuffer(gId);
+
+};
+
+/**
+ * Draw a sphere with given raduis
+ * @method sphere
+ * @param  {Number} radius            radius of circle
+ * @param  {Number} [detail]          number of segments,
+ *                                    the more segments the smoother geometry
+ *                                    default is 24. Avoid detail number above
+ *                                    150, it may crash the browser.
+ * @example
+ * <div>
+ * <code>
+ * // draw a sphere with radius 200
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.sphere = function(radius, detail){
+
+  radius = radius || 50;
+
+  var detailX = detail || 24;
+  var detailY = detail || 16;
+
+  var gId = 'sphere|'+radius+'|'+detailX+'|'+detailY;
+
+  if(!this._renderer.geometryInHash(gId)){
+
+    var geometry3d = new p5.Geometry3D();
+
+    var createSphere = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var phi = Math.PI * v - Math.PI / 2;
+      var x = radius * Math.cos(phi) * Math.sin(theta);
+      var y = radius * Math.sin(phi);
+      var z = radius * Math.cos(phi) * Math.cos(theta);
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d.parametricGeometry(createSphere, detailX, detailY);
+
+    var obj = geometry3d.generateObj(true, true);
+
+    this._renderer.initBuffer(gId, [obj]);
+  }
+
+  this._renderer.drawBuffer(gId);
+
+  return this;
+};
+
+/**
+ * Draw an ellipsoid with given radius
+ * @method ellipsoid
+ * @param  {Number} radiusx           xradius of circle
+ * @param  {Number} radiusy           yradius of circle
+ * @param  {Number} radiusz           zradius of circle
+ * @param  {Number} [detail]          Number of segments.
+ *                                    The more segments, the smoother the
+ *                                    geometry (default is 24). Avoid detail
+ *                                    number above 150. It may crash the
+ *                                    browser.
+ * @return {p5}                       the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // draw an ellipsoid with radius 200, 300 and 400
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   ellipsoid(200,300,400);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ellipsoid = function(radiusx, radiusy, radiusz, detail){
+
+  radiusx = radiusx || 50;
+  radiusy = radiusy || 50;
+  radiusz = radiusz || 50;
+
+  var detailX = detail || 24;
+  var detailY = detail || 24;
+
+  var gId = 'ellipsoid|'+radiusx+'|'+radiusy+
+  '|'+radiusz+'|'+detailX+'|'+detailY;
+
+
+  if(!this._renderer.geometryInHash(gId)){
+
+    var geometry3d = new p5.Geometry3D();
+
+    var createEllipsoid = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var phi = Math.PI * v - Math.PI / 2;
+      var x = radiusx * Math.cos(phi) * Math.sin(theta);
+      var y = radiusy * Math.sin(phi);
+      var z = radiusz * Math.cos(phi) * Math.cos(theta);
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d.parametricGeometry(createEllipsoid, detailX, detailY);
+
+    var obj = geometry3d.generateObj(true, true);
+
+    this._renderer.initBuffer(gId, [obj]);
+  }
+
+  this._renderer.drawBuffer(gId);
+
+  return this;
+};
+
+/**
+ * Draw a cylinder with given radius and height
+ * @method  cylinder
+ * @param  {Number} radius            radius of the surface
+ * @param  {Number} height            height of the cylinder
+ * @param  {Number} [detail]          number of segments,
+ *                                    the more segments the smoother geometry
+ *                                    default is 24. Avoid detail number above
+ *                                    150. It may crash the browser.
+ * @return {p5}                       the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning cylinder with radius 200 and height 200
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   rotateX(frameCount * 0.01);
+ *   rotateZ(frameCount * 0.01);
+ *   cylinder(200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.cylinder = function(radius, height, detail){
+
+  radius = radius || 50;
+  height = height || 50;
+
+  var detailX = detail || 24;
+  var detailY = detail || 16;
+
+  var gId = 'cylinder|'+radius+'|'+height+'|'+detailX+'|'+detailY;
+
+  if(!this._renderer.geometryInHash(gId)){
+
+    var geometry3d = new p5.Geometry3D();
+
+    var createCylinder = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var x = radius * Math.sin(theta);
+      var y = 2 * height * v - height;
+      var z = radius * Math.cos(theta);
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d.parametricGeometry(createCylinder, detailX, detailY);
+    var obj = geometry3d.generateObj(true);
+
+    var createTop = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var x = radius * Math.sin(-theta);
+      var y = height;
+      var z = radius * Math.cos(theta);
+      if(v === 0){
+        return new p5.Vector(0, height, 0);
+      }
+      else{
+        return new p5.Vector(x, y, z);
+      }
+    };
+
+    var geometry3d1 = new p5.Geometry3D();
+    geometry3d1.parametricGeometry(
+      createTop, detailX, 1);
+    var obj1 = geometry3d1.generateObj();
+
+    var createBottom = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var x = radius * Math.sin(theta);
+      var y = -height;
+      var z = radius * Math.cos(theta);
+      if(v === 0){
+        return new p5.Vector(0, -height, 0);
+      }else{
+        return new p5.Vector(x, y, z);
+      }
+    };
+
+    var geometry3d2 = new p5.Geometry3D();
+    geometry3d2.parametricGeometry(
+      createBottom, detailX, 1);
+    var obj2 = geometry3d2.generateObj();
+
+
+    this._renderer.initBuffer(gId, [obj, obj1, obj2]);
+  }
+
+  this._renderer.drawBuffer(gId);
+
+  return this;
+};
+
+
+/**
+ * Draw a cone with given radius and height
+ * @method cone
+ * @param  {Number} radius            radius of the bottom surface
+ * @param  {Number} height            height of the cone
+ * @param  {Number} [detail]          number of segments,
+ *                                    the more segments the smoother geometry
+ *                                    default is 24. Avoid detail number above
+ *                                    150. It may crash the browser.
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning cone with radius 200 and height 200
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   rotateX(frameCount * 0.01);
+ *   rotateZ(frameCount * 0.01);
+ *   cone(200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.cone = function(radius, height, detail){
+
+  radius = radius || 50;
+  height = height || 50;
+
+  var detailX = detail || 24;
+  var detailY = detail || 16;
+
+  var gId = 'cone|'+radius+'|'+height+'|'+detailX+'|'+detailY;
+
+  if(!this._renderer.geometryInHash(gId)){
+
+    var geometry3d = new p5.Geometry3D();
+
+    var createCone = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var x = radius * (1 - v) * Math.sin(theta);
+      var y = 2 * height * v - height;
+      var z = radius * (1 - v) * Math.cos(theta);
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d.parametricGeometry(createCone, detailX, detailY);
+    var obj = geometry3d.generateObj(true);
+
+    var geometry3d1 = new p5.Geometry3D();
+    var createBottom = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var x = radius * (1 - v) * Math.sin(-theta);
+      var y = -height;
+      var z = radius * (1 - v) * Math.cos(theta);
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d1.parametricGeometry(
+      createBottom, detailX, 1);
+    var obj1 = geometry3d1.generateObj();
+
+    this._renderer.initBuffer(gId, [obj, obj1]);
+  }
+
+  this._renderer.drawBuffer(gId);
+
+  return this;
+};
+
+
+/**
+ * Draw a torus with given radius and tube radius
+ * @method torus
+ * @param  {Number} radius            radius of the whole ring
+ * @param  {Number} tubeRadius        radius of the tube
+ * @param  {Number} [detail]          number of segments,
+ *                                    the more segments the smoother geometry
+ *                                    default is 24. Avoid detail number above
+ *                                    150. It may crash the browser.
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning torus with radius 200 and tube radius 60
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   rotateX(frameCount * 0.01);
+ *   rotateY(frameCount * 0.01);
+ *   torus(200, 60);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.torus = function(radius, tubeRadius, detail){
+
+  radius = radius || 50;
+  tubeRadius = tubeRadius || 10;
+
+  var detailX = detail || 24;
+  var detailY = detail || 16;
+
+  var gId = 'torus|'+radius+'|'+tubeRadius+'|'+detailX+'|'+detailY;
+
+  if(!this._renderer.geometryInHash(gId)){
+
+    var geometry3d = new p5.Geometry3D();
+
+    var createTorus = function(u, v){
+      var theta = 2 * Math.PI * u;
+      var phi = 2 * Math.PI * v;
+      var x = (radius + tubeRadius * Math.cos(phi)) * Math.cos(theta);
+      var y = (radius + tubeRadius * Math.cos(phi)) * Math.sin(theta);
+      var z = tubeRadius * Math.sin(phi);
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d.parametricGeometry(createTorus, detailX, detailY);
+
+    var obj = geometry3d.generateObj(true);
+
+    this._renderer.initBuffer(gId, [obj]);
+  }
+
+  this._renderer.drawBuffer(gId);
+
+  return this;
+};
+
+/**
+ * Draw a box with given width, height and depth
+ * @method  box
+ * @param  {Number} width  width of the box
+ * @param  {Number} height height of the box
+ * @param  {Number} depth  depth of the box
+ * @return {p5}            the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning box with width, height and depth 200
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   rotateX(frameCount * 0.01);
+ *   rotateY(frameCount * 0.01);
+ *   box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.box = function(width, height, depth){
+
+  width = width || 50;
+  height = height || width;
+  depth = depth || width;
+
+  //details for box are highly optional
+  var detailX = typeof arguments[3] === Number ? arguments[3] : 1;
+  var detailY = typeof arguments[4] === Number ? arguments[4] : 1;
+
+  var gId = 'cube|'+width+'|'+height+'|'+depth+'|'+detailX+'|'+detailY;
+
+  if(!this._renderer.geometryInHash(gId)){
+
+    var geometry3d = new p5.Geometry3D();
+
+    var createPlane1 = function(u, v){
+      var x = 2 * width * u - width;
+      var y = 2 * height * v - height;
+      var z = depth;
+      return new p5.Vector(x, y, z);
+    };
+    var createPlane2 = function(u, v){
+      var x = 2 * width * ( 1 - u ) - width;
+      var y = 2 * height * v - height;
+      var z = -depth;
+      return new p5.Vector(x, y, z);
+    };
+    var createPlane3 = function(u, v){
+      var x = 2 * width * ( 1 - u ) - width;
+      var y = height;
+      var z = 2 * depth * v - depth;
+      return new p5.Vector(x, y, z);
+    };
+    var createPlane4 = function(u, v){
+      var x = 2 * width * u - width;
+      var y = -height;
+      var z = 2 * depth * v - depth;
+      return new p5.Vector(x, y, z);
+    };
+    var createPlane5 = function(u, v){
+      var x = width;
+      var y = 2 * height * u - height;
+      var z = 2 * depth * v - depth;
+      return new p5.Vector(x, y, z);
+    };
+    var createPlane6 = function(u, v){
+      var x = -width;
+      var y = 2 * height * ( 1 - u ) - height;
+      var z = 2 * depth * v - depth;
+      return new p5.Vector(x, y, z);
+    };
+
+    geometry3d.parametricGeometry(
+      createPlane1, detailX, detailY, geometry3d.vertices.length);
+    geometry3d.parametricGeometry(
+      createPlane2, detailX, detailY, geometry3d.vertices.length);
+    geometry3d.parametricGeometry(
+      createPlane3, detailX, detailY, geometry3d.vertices.length);
+    geometry3d.parametricGeometry(
+      createPlane4, detailX, detailY, geometry3d.vertices.length);
+    geometry3d.parametricGeometry(
+      createPlane5, detailX, detailY, geometry3d.vertices.length);
+    geometry3d.parametricGeometry(
+      createPlane6, detailX, detailY, geometry3d.vertices.length);
+
+    var obj = geometry3d.generateObj();
+
+    this._renderer.initBuffer(gId, [obj]);
+  }
+
+  this._renderer.drawBuffer(gId);
+
+  return this;
+
+};
+
+module.exports = p5;
+},{"../core/core":48,"./p5.Geometry3D":34}],29:[function(_dereq_,module,exports){
+/**
+ * @module Lights, Camera
+ * @submodule Camera
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Sets camera position
+ * @method camera
+ * @param  {Number} x  camera postion value on x axis
+ * @param  {Number} y  camera postion value on y axis
+ * @param  {Number} z  camera postion value on z axis
+ * @return {p5}        the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ *  //move the camera away from the plane by a sin wave
+ *  camera(0, 0, sin(frameCount * 0.01) * 100);
+ *  plane(120, 120);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.camera = function(x, y, z){
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'camera',
+    args,
+    ['Number', 'Number', 'Number']
+  );
+  //what it manipulates is the model view matrix
+  this._renderer.translate(-x, -y, -z);
+};
+
+/**
+ * Sets perspective camera
+ * @method  perspective
+ * @param  {Number} fovy   camera frustum vertical field of view,
+ *                         from bottom to top of view, in degrees
+ * @param  {Number} aspect camera frustum aspect ratio
+ * @param  {Number} near   frustum near plane length
+ * @param  {Number} far    frustum far plane length
+ * @return {p5}            the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //drag mouse to toggle the world!
+ * //you will see there's a vanish point
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ *   perspective(60 / 180 * PI, width/height, 0.1, 100);
+ * }
+ * function draw(){
+ *  background(200);
+ *  orbitControl();
+ *  for(var i = -1; i < 2; i++){
+ *     for(var j = -2; j < 3; j++){
+ *       push();
+ *       translate(i*160, 0, j*160);
+ *       box(40, 40, 40);
+ *       pop();
+ *     }
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.perspective = function(fovy,aspect,near,far) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'perspective',
+    args,
+    ['Number', 'Number', 'Number', 'Number']
+  );
+  this._renderer.uPMatrix = p5.Matrix.identity();
+  this._renderer.uPMatrix.perspective(fovy,aspect,near,far);
+  this._renderer._setCamera = true;
+};
+
+/**
+ * Setup ortho camera
+ * @method  ortho
+ * @param  {Number} left   camera frustum left plane
+ * @param  {Number} right  camera frustum right plane
+ * @param  {Number} bottom camera frustum bottom plane
+ * @param  {Number} top    camera frustum top plane
+ * @param  {Number} near   camera frustum near plane
+ * @param  {Number} far    camera frustum far plane
+ * @return {p5}            the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //drag mouse to toggle the world!
+ * //there's no vanish point
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ *   ortho(-width/2, width/2, height/2, -height/2, 0.1, 100);
+ * }
+ * function draw(){
+ *  background(200);
+ *  orbitControl();
+ *  for(var i = -1; i < 2; i++){
+ *     for(var j = -2; j < 3; j++){
+ *       push();
+ *       translate(i*160, 0, j*160);
+ *       box(40, 40, 40);
+ *       pop();
+ *     }
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ortho = function(left,right,bottom,top,near,far) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'ortho',
+    args,
+      ['Number', 'Number', 'Number', 'Number', 'Number', 'Number']
+  );
+  left /= this.width;
+  right /= this.width;
+  top /= this.height;
+  bottom /= this.height;
+  this._renderer.uPMatrix = p5.Matrix.identity();
+  this._renderer.uPMatrix.ortho(left,right,bottom,top,near,far);
+  this._renderer._setCamera = true;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],30:[function(_dereq_,module,exports){
+//@TODO: documentation of immediate mode
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+//////////////////////////////////////////////
+// _primitives2D in 3D space
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype._primitives2D = function(arr){
+  this._setDefaultCamera();
+  var gl = this.GL;
+  var shaderProgram = this._getColorVertexShader();
+
+  //create vertice buffer
+  var vertexPositionBuffer = this.verticeBuffer;
+  gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
+
+  gl.bufferData(
+    gl.ARRAY_BUFFER, new Float32Array(arr), gl.STATIC_DRAW);
+  gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
+    3, gl.FLOAT, false, 0, 0);
+
+  //create vertexcolor buffer
+  var vertexColorBuffer = this.colorBuffer;
+  gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
+  var color = this._getCurColor();
+  var colors = [];
+  for(var i = 0; i < arr.length / 3; i++){
+    colors = colors.concat(color);
+  }
+
+  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
+  gl.vertexAttribPointer(shaderProgram.vertexColorAttribute,
+    4, gl.FLOAT, false, 0, 0);
+
+  //matrix
+  var mId = 'vertexColorVert|vertexColorFrag';
+  this.setMatrixUniforms(mId);
+};
+
+p5.Renderer3D.prototype.point = function(x, y, z){
+  var gl = this.GL;
+  this._primitives2D([x, y, z]);
+  gl.drawArrays(gl.POINTS, 0, 1);
+  return this;
+};
+
+p5.Renderer3D.prototype.line = function(x1, y1, z1, x2, y2, z2){
+  var gl = this.GL;
+  this._primitives2D([x1, y1, z1, x2, y2, z2]);
+  gl.drawArrays(gl.LINES, 0, 2);
+  return this;
+};
+
+p5.Renderer3D.prototype.triangle = function
+(x1, y1, z1, x2, y2, z2, x3, y3, z3){
+  var gl = this.GL;
+  this._primitives2D([x1, y1, z1, x2, y2, z2, x3, y3, z3]);
+  this._strokeCheck();
+  gl.drawArrays(gl.TRIANGLES, 0, 3);
+  return this;
+};
+
+//@TODO: how to define the order of 4 points
+p5.Renderer3D.prototype.quad = function
+(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4){
+  var gl = this.GL;
+  this._primitives2D(
+    [x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4]);
+  this._strokeCheck();
+  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+  return this;
+};
+
+p5.Renderer3D.prototype.beginShape = function(mode){
+  this.shapeMode = mode;
+  this.verticeStack = [];
+  return this;
+};
+
+p5.Renderer3D.prototype.vertex = function(x, y, z){
+  this.verticeStack.push(x, y, z);
+  return this;
+};
+
+p5.Renderer3D.prototype.endShape = function(){
+  var gl = this.GL;
+  this._primitives2D(this.verticeStack);
+  this.verticeStack = [];
+
+  switch(this.shapeMode){
+    case 'POINTS':
+      gl.drawArrays(gl.POINTS, 0, 1);
+      break;
+    case 'LINES':
+      gl.drawArrays(gl.LINES, 0, 2);
+      break;
+    case 'TRIANGLES':
+      this._strokeCheck();
+      gl.drawArrays(gl.TRIANGLES, 0, 3);
+      break;
+    case 'TRIANGLE_STRIP':
+      this._strokeCheck();
+      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+      break;
+    default:
+      this._strokeCheck();
+      gl.drawArrays(gl.TRIANGLES, 0, 3);
+      break;
+  }
+  return this;
+};
+
+//@TODO: figure out how to actually do stroke on shapes in 3D
+p5.Renderer3D.prototype._strokeCheck = function(){
+  if(this.drawMode === 'stroke'){
+    throw new Error(
+      'stroke for shapes in 3D not yet implemented, use fill for now :('
+    );
+  }
+};
+
+//@TODO
+p5.Renderer3D.prototype.strokeWeight = function() {
+  throw new Error('strokeWeight for 3d not yet implemented');
+};
+
+//////////////////////////////////////////////
+// COLOR
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype.fill = function(r, g, b, a) {
+  var color = this._pInst.color.apply(this, arguments);
+  var colorNormalized = color._array;
+  this.curColor = colorNormalized;
+  this.drawMode = 'fill';
+  return this;
+};
+
+p5.Renderer3D.prototype.stroke = function(r, g, b, a) {
+  var color = this._pInst.color.apply(this, arguments);
+  var colorNormalized = color._array;
+  this.curColor = colorNormalized;
+  this.drawMode = 'stroke';
+  return this;
+};
+
+p5.Renderer3D.prototype._getColorVertexShader = function(){
+  var gl = this.GL;
+  var mId = 'vertexColorVert|vertexColorFrag';
+  var shaderProgram;
+
+  if(!this.materialInHash(mId)){
+    shaderProgram =
+      this.initShaders('vertexColorVert', 'vertexColorFrag', true);
+    this.mHash[mId] = shaderProgram;
+    shaderProgram.vertexColorAttribute =
+    gl.getAttribLocation(shaderProgram, 'aVertexColor');
+    gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
+  }else{
+    shaderProgram = this.mHash[mId];
+  }
+  return shaderProgram;
+};
+
+module.exports = p5.Renderer3D;
+
+},{"../core/core":48}],31:[function(_dereq_,module,exports){
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+//@TODO: implement full orbit controls including
+//pan, zoom, quaternion rotation, etc.
+p5.prototype.orbitControl = function(){
+  if(this.mouseIsPressed){
+    this.rotateY((this.mouseX - this.width / 2) / (this.width / 2));
+    this.rotateX((this.mouseY - this.height / 2) / (this.width / 2));
+  }
+  return this;
+};
+
+module.exports = p5;
+},{"../core/core":48}],32:[function(_dereq_,module,exports){
+/**
+ * @module Lights, Camera
+ * @submodule Lights
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Creates an ambient light with a color
+ * @method  ambientLight
+ * @param  {Number|Array|String|p5.Color} v1  gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param  {Number}            [v2] optional: green or saturation value
+ * @param  {Number}            [v3] optional: blue or brightness value
+ * @param  {Number}            [a]  optional: opacity
+ * @return {p5}                the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ *   background(0);
+ *   ambientLight(150);
+ *   ambientMaterial(250);
+ *   sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ambientLight = function(v1, v2, v3, a){
+  var gl = this._renderer.GL;
+  var shaderProgram = this._renderer._getShader(
+    'lightVert', 'lightTextureFrag');
+
+  gl.useProgram(shaderProgram);
+  shaderProgram.uAmbientColor = gl.getUniformLocation(
+    shaderProgram,
+    'uAmbientColor[' + this._renderer.ambientLightCount + ']');
+
+  var color = this._renderer._pInst.color.apply(
+    this._renderer._pInst, arguments);
+  var colors = color._array;
+
+  gl.uniform3f( shaderProgram.uAmbientColor,
+    colors[0], colors[1], colors[2]);
+
+  //in case there's no material color for the geometry
+  shaderProgram.uMaterialColor = gl.getUniformLocation(
+    shaderProgram, 'uMaterialColor' );
+  gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1);
+
+  this._renderer.ambientLightCount ++;
+  shaderProgram.uAmbientLightCount =
+    gl.getUniformLocation(shaderProgram, 'uAmbientLightCount');
+  gl.uniform1i(shaderProgram.uAmbientLightCount,
+    this._renderer.ambientLightCount);
+
+  return this;
+};
+
+/**
+ * Creates a directional light with a color and a direction
+ * @method  directionalLight
+ * @param  {Number|Array|String|p5.Color} v1   gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param  {Number}          [v2] optional: green or saturation value
+ * @param  {Number}          [v3] optional: blue or brightness value
+ * @param  {Number}          [a]  optional: opacity
+ * @param  {Number|p5.Vector} x   x axis direction or a p5.Vector
+ * @param  {Number}          [y]  optional: y axis direction
+ * @param  {Number}          [z]  optional: z axis direction
+ * @return {p5}              the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ *   background(0);
+ *   //move your mouse to change light direction
+ *   var dirX = (mouseX / width - 0.5) *2;
+ *   var dirY = (mouseY / height - 0.5) *(-2);
+ *   directionalLight(250, 250, 250, dirX, dirY, 0.25);
+ *   ambientMaterial(250);
+ *   sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.directionalLight = function(v1, v2, v3, a, x, y, z) {
+  // TODO(jgessner): Find an example using this and profile it.
+  // var args = new Array(arguments.length);
+  // for (var i = 0; i < args.length; ++i) {
+  //   args[i] = arguments[i];
+  // }
+  // this._validateParameters(
+  //   'directionalLight',
+  //   args,
+  //   [
+  //     //rgbaxyz
+  //     ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+  //     //rgbxyz
+  //     ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+  //     //caxyz
+  //     ['Number', 'Number', 'Number', 'Number', 'Number'],
+  //     //cxyz
+  //     ['Number', 'Number', 'Number', 'Number'],
+  //     ['String', 'Number', 'Number', 'Number'],
+  //     ['Array', 'Number', 'Number', 'Number'],
+  //     ['Object', 'Number', 'Number', 'Number'],
+  //     //rgbavector
+  //     ['Number', 'Number', 'Number', 'Number', 'Object'],
+  //     //rgbvector
+  //     ['Number', 'Number', 'Number', 'Object'],
+  //     //cavector
+  //     ['Number', 'Number', 'Object'],
+  //     //cvector
+  //     ['Number', 'Object'],
+  //     ['String', 'Object'],
+  //     ['Array', 'Object'],
+  //     ['Object', 'Object']
+  //   ]
+  // );
+
+  var gl = this._renderer.GL;
+  var shaderProgram = this._renderer._getShader(
+    'lightVert', 'lightTextureFrag');
+
+  gl.useProgram(shaderProgram);
+  shaderProgram.uDirectionalColor = gl.getUniformLocation(
+    shaderProgram,
+    'uDirectionalColor[' + this._renderer.directionalLightCount + ']');
+
+  //@TODO: check parameters number
+  var color = this._renderer._pInst.color.apply(
+    this._renderer._pInst, [v1, v2, v3]);
+  var colors = color._array;
+
+  gl.uniform3f( shaderProgram.uDirectionalColor,
+    colors[0], colors[1], colors[2]);
+
+  var _x, _y, _z;
+
+  if(typeof arguments[arguments.length-1] === 'number'){
+    _x = arguments[arguments.length-3];
+    _y = arguments[arguments.length-2];
+    _z = arguments[arguments.length-1];
+
+  }else{
+    try{
+      _x = arguments[arguments.length-1].x;
+      _y = arguments[arguments.length-1].y;
+      _z = arguments[arguments.length-1].z;
+    }
+    catch(error){
+      throw error;
+    }
+  }
+
+  shaderProgram.uLightingDirection = gl.getUniformLocation(
+    shaderProgram,
+    'uLightingDirection[' + this._renderer.directionalLightCount + ']');
+  gl.uniform3f( shaderProgram.uLightingDirection, _x, _y, _z);
+
+  //in case there's no material color for the geometry
+  shaderProgram.uMaterialColor = gl.getUniformLocation(
+    shaderProgram, 'uMaterialColor' );
+  gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1);
+
+  this._renderer.directionalLightCount ++;
+  shaderProgram.uDirectionalLightCount =
+    gl.getUniformLocation(shaderProgram, 'uDirectionalLightCount');
+  gl.uniform1i(shaderProgram.uDirectionalLightCount,
+    this._renderer.directionalLightCount);
+
+  return this;
+};
+
+/**
+ * Creates a point light with a color and a light position
+ * @method  pointLight
+ * @param  {Number|Array|String|p5.Color} v1   gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param  {Number}          [v2] optional: green or saturation value
+ * @param  {Number}          [v3] optional: blue or brightness value
+ * @param  {Number}          [a]  optional: opacity
+ * @param  {Number|p5.Vector} x   x axis position or a p5.Vector
+ * @param  {Number}          [y]  optional: y axis position
+ * @param  {Number}          [z]  optional: z axis position
+ * @return {p5}              the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ *   background(0);
+ *   //move your mouse to change light position
+ *   var locY = (mouseY / height - 0.5) *(-2);
+ *   var locX = (mouseX / width - 0.5) *2;
+ *   //to set the light position,
+ *   //think of the world's coordinate as:
+ *   // -1,1 -------- 1,1
+ *   //   |            |
+ *   //   |            |
+ *   //   |            |
+ *   // -1,-1---------1,-1
+ *   pointLight(250, 250, 250, locX, locY, 0);
+ *   ambientMaterial(250);
+ *   sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.pointLight = function(v1, v2, v3, a, x, y, z) {
+  // TODO(jgessner): Find an example using this and profile it.
+  // var args = new Array(arguments.length);
+  // for (var i = 0; i < args.length; ++i) {
+  //   args[i] = arguments[i];
+  // }
+  // this._validateParameters(
+  //   'pointLight',
+  //   arguments,
+  //   [
+  //     //rgbaxyz
+  //     ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+  //     //rgbxyz
+  //     ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+  //     //caxyz
+  //     ['Number', 'Number', 'Number', 'Number', 'Number'],
+  //     //cxyz
+  //     ['Number', 'Number', 'Number', 'Number'],
+  //     ['String', 'Number', 'Number', 'Number'],
+  //     ['Array', 'Number', 'Number', 'Number'],
+  //     ['Object', 'Number', 'Number', 'Number'],
+  //     //rgbavector
+  //     ['Number', 'Number', 'Number', 'Number', 'Object'],
+  //     //rgbvector
+  //     ['Number', 'Number', 'Number', 'Object'],
+  //     //cavector
+  //     ['Number', 'Number', 'Object'],
+  //     //cvector
+  //     ['Number', 'Object'],
+  //     ['String', 'Object'],
+  //     ['Array', 'Object'],
+  //     ['Object', 'Object']
+  //   ]
+  // );
+
+  var gl = this._renderer.GL;
+  var shaderProgram = this._renderer._getShader(
+    'lightVert', 'lightTextureFrag');
+
+  gl.useProgram(shaderProgram);
+  shaderProgram.uPointLightColor = gl.getUniformLocation(
+    shaderProgram,
+    'uPointLightColor[' + this._renderer.pointLightCount + ']');
+
+  //@TODO: check parameters number
+  var color = this._renderer._pInst.color.apply(
+    this._renderer._pInst, [v1, v2, v3]);
+  var colors = color._array;
+
+  gl.uniform3f( shaderProgram.uPointLightColor,
+    colors[0], colors[1], colors[2]);
+
+  var _x, _y, _z;
+
+  if(typeof arguments[arguments.length-1] === 'number'){
+    _x = arguments[arguments.length-3];
+    _y = arguments[arguments.length-2];
+    _z = arguments[arguments.length-1];
+
+  }else{
+    try{
+      _x = arguments[arguments.length-1].x;
+      _y = arguments[arguments.length-1].y;
+      _z = arguments[arguments.length-1].z;
+    }
+    catch(error){
+      throw error;
+    }
+  }
+
+  shaderProgram.uPointLightLocation = gl.getUniformLocation(
+    shaderProgram,
+    'uPointLightLocation[' + this._renderer.pointLightCount + ']');
+  gl.uniform3f( shaderProgram.uPointLightLocation, _x, _y, _z);
+
+  //in case there's no material color for the geometry
+  shaderProgram.uMaterialColor = gl.getUniformLocation(
+    shaderProgram, 'uMaterialColor' );
+  gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1);
+
+  this._renderer.pointLightCount ++;
+  shaderProgram.uPointLightCount =
+    gl.getUniformLocation(shaderProgram, 'uPointLightCount');
+  gl.uniform1i(shaderProgram.uPointLightCount,
+    this._renderer.pointLightCount);
+
+  return this;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],33:[function(_dereq_,module,exports){
+/**
+ * @module Lights, Camera
+ * @submodule Material
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+//require('./p5.Texture');
+
+/**
+ * Normal material for geometry
+ * @method normalMaterial
+ * @return {p5}                the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *  background(0);
+ *  normalMaterial();
+ *  sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.normalMaterial = function(){
+  this._renderer._getShader('normalVert', 'normalFrag');
+  return this;
+};
+
+/**
+ * Texture for geometry
+ * @method texture
+ * @return {p5}                the p5 object
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ *   img = loadImage("assets/laDefense.jpg");
+ * }
+ *
+ * function draw(){
+ *   background(0);
+ *   rotateZ(frameCount * 0.01);
+ *   rotateX(frameCount * 0.01);
+ *   rotateY(frameCount * 0.01);
+ *   //pass image as texture
+ *   texture(img);
+ *   box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.texture = function(image){
+  var gl = this._renderer.GL;
+  var shaderProgram = this._renderer._getShader('lightVert',
+    'lightTextureFrag');
+  gl.useProgram(shaderProgram);
+  if (image instanceof p5.Image) {
+    //check if image is already used as texture
+    if(!image.isTexture){
+      //createTexture and set isTexture to true
+      var tex = gl.createTexture();
+      image.createTexture(tex);
+      gl.bindTexture(gl.TEXTURE_2D, tex);
+      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
+      image._setProperty('isTexture', true);
+    }
+    //otherwise we're good to bind texture without creating
+    //a new one on the gl
+    else {
+      //TODO
+    }
+    image.loadPixels();
+    var data = new Uint8Array(image.pixels);
+    gl.texImage2D(gl.TEXTURE_2D, 0,
+      gl.RGBA, image.width, image.height,
+      0, gl.RGBA, gl.UNSIGNED_BYTE, data);
+  }
+  //if param is a video
+  else if (image instanceof p5.MediaElement){
+    if(!image.loadedmetadata) {return;}
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
+    gl.UNSIGNED_BYTE, image.elt);
+  }
+  else {
+    //@TODO handle following cases:
+    //- 2D canvas (p5 inst)
+  }
+  if (_isPowerOf2(image.width) && _isPowerOf2(image.height)) {
+    gl.generateMipmap(gl.TEXTURE_2D);
+  } else {
+    //@TODO this is problematic
+    //image.width = _nextHighestPOT(image.width);
+    //image.height = _nextHighestPOT(image.height);
+    gl.texParameteri(gl.TEXTURE_2D,
+    gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D,
+    gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D,
+    gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+    gl.texParameteri(gl.TEXTURE_2D,
+    gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+  }
+  //this is where we'd activate multi textures
+  //eg. gl.activeTexture(gl.TEXTURE0 + (unit || 0));
+  //but for now we just have a single texture.
+  //@TODO need to extend this functionality
+  //gl.activeTexture(gl.TEXTURE0 + 0);
+  //gl.bindTexture(gl.TEXTURE_2D, tex);
+  gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0);
+  gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), true);
+  return this;
+};
+
+/**
+ * Helper functions; Checks whether val is a pot
+ * more info on power of 2 here:
+ * https://www.opengl.org/wiki/NPOT_Texture
+ * @param  {Number}  value
+ * @return {Boolean}
+ */
+function _isPowerOf2 (value){
+  return (value & (value - 1)) === 0;
+}
+
+/**
+ * returns the next highest power of 2 value
+ * @param  {Number} value [description]
+ * @return {Number}       [description]
+ */
+// function _nextHighestPOT (value){
+//   --value;
+//   for (var i = 1; i < 32; i <<= 1) {
+//     value = value | value >> i;
+//   }
+//   return value + 1;
+// }
+
+/**
+ * Basic material for geometry with a given color
+ * @method  basicMaterial
+ * @param  {Number|Array|String|p5.Color} v1  gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param  {Number}            [v2] optional: green or saturation value
+ * @param  {Number}            [v3] optional: blue or brightness value
+ * @param  {Number}            [a]  optional: opacity
+ * @return {p5}                the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *  background(0);
+ *  basicMaterial(250, 0, 0);
+ *  rotateX(frameCount * 0.01);
+ *  rotateY(frameCount * 0.01);
+ *  rotateZ(frameCount * 0.01);
+ *  box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.basicMaterial = function(v1, v2, v3, a){
+  var gl = this._renderer.GL;
+
+  var shaderProgram = this._renderer._getShader('normalVert', 'basicFrag');
+
+  gl.useProgram(shaderProgram);
+  shaderProgram.uMaterialColor = gl.getUniformLocation(
+    shaderProgram, 'uMaterialColor' );
+
+  var color = this._renderer._pInst.color.apply(
+    this._renderer._pInst, arguments);
+  var colors = color._array;
+
+  gl.uniform4f( shaderProgram.uMaterialColor,
+    colors[0], colors[1], colors[2], colors[3]);
+
+  return this;
+
+};
+
+/**
+ * Ambient material for geometry with a given color
+ * @method  ambientMaterial
+ * @param  {Number|Array|String|p5.Color} v1  gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param  {Number}            [v2] optional: green or saturation value
+ * @param  {Number}            [v3] optional: blue or brightness value
+ * @param  {Number}            [a]  optional: opacity
+* @return {p5}                 the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ *  background(0);
+ *  ambientLight(100);
+ *  pointLight(250, 250, 250, 100, 100, 0);
+ *  ambientMaterial(250);
+ *  sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ambientMaterial = function(v1, v2, v3, a) {
+  var gl = this._renderer.GL;
+  var shaderProgram =
+    this._renderer._getShader('lightVert', 'lightTextureFrag');
+
+  gl.useProgram(shaderProgram);
+  shaderProgram.uMaterialColor = gl.getUniformLocation(
+    shaderProgram, 'uMaterialColor' );
+
+  var color = this._renderer._pInst.color.apply(
+    this._renderer._pInst, arguments);
+  var colors = color._array;
+
+  gl.uniform4f(shaderProgram.uMaterialColor,
+    colors[0], colors[1], colors[2], colors[3]);
+
+  shaderProgram.uSpecular = gl.getUniformLocation(
+    shaderProgram, 'uSpecular' );
+  gl.uniform1i(shaderProgram.uSpecular, false);
+
+  gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false);
+
+  return this;
+};
+
+/**
+ * Specular material for geometry with a given color
+ * @method specularMaterial
+ * @param  {Number|Array|String|p5.Color} v1  gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param  {Number}            [v2] optional: green or saturation value
+ * @param  {Number}            [v3] optional: blue or brightness value
+ * @param  {Number}            [a]  optional: opacity
+ * @return {p5}                the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ *  background(0);
+ *  ambientLight(100);
+ *  pointLight(250, 250, 250, 100, 100, 0);
+ *  specularMaterial(250);
+ *  sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.specularMaterial = function(v1, v2, v3, a) {
+  var gl = this._renderer.GL;
+  var shaderProgram =
+    this._renderer._getShader('lightVert', 'lightTextureFrag');
+  gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false);
+  gl.useProgram(shaderProgram);
+  shaderProgram.uMaterialColor = gl.getUniformLocation(
+    shaderProgram, 'uMaterialColor' );
+
+  var color = this._renderer._pInst.color.apply(
+    this._renderer._pInst, arguments);
+  var colors = color._array;
+
+  gl.uniform4f(shaderProgram.uMaterialColor,
+    colors[0], colors[1], colors[2], colors[3]);
+
+  shaderProgram.uSpecular = gl.getUniformLocation(
+    shaderProgram, 'uSpecular' );
+  gl.uniform1i(shaderProgram.uSpecular, true);
+
+  return this;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],34:[function(_dereq_,module,exports){
+//some of the functions are adjusted from Three.js(http://threejs.org)
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * p5 Geometry3D class
+ */
+p5.Geometry3D = function(){
+  //an array holding every vertice
+  //each vertex is a p5.Vector
+  this.vertices = [];
+  //an array holding each normals for each vertice
+  //each normal is a p5.Vector
+  this.vertexNormals = [];
+  //an array holding each three indecies of vertices that form a face
+  //[[0, 1, 2], [1, 2, 3], ...]
+  this.faces = [];
+  //an array holding every noraml for each face
+  //each faceNormal is a p5.Vector
+  //[[p5.Vector, p5.Vector, p5.Vector],[p5.Vector, p5.Vector, p5.Vector],...]
+  this.faceNormals = [];
+  //an array of array holding uvs (group according to faces)
+  //[[[0, 0], [1, 0], [1, 0]],...]
+  this.uvs = [];
+};
+
+/**
+ * generate geometriy with parametric method
+ * @param  {Function} func  callback function for how to generate geometry
+ * @param  {Number} detailX number of vertices on horizontal surface
+ * @param  {Number} detailY number of vertices on horizontal surface
+ * @param  {Number} offset  offset of vertices index
+ */
+p5.Geometry3D.prototype.parametricGeometry = function
+//@TODO: put func as the last parameters
+(func, detailX, detailY, offset){
+
+  var i, j, p;
+  var u, v;
+  offset = offset || 0;
+  this.detailX = detailX;
+  this.detailY = detailY;
+
+  var sliceCount = detailX + 1;
+  for (i = 0; i <= detailY; i++){
+    v = i / detailY;
+    for (j = 0; j <= detailX; j++){
+      u = j / detailX;
+      p = func(u, v);
+      this.vertices.push(p);
+    }
+  }
+
+  var a, b, c, d;
+  var uva, uvb, uvc, uvd;
+
+  for (i = 0; i < detailY; i++){
+    for (j = 0; j < detailX; j++){
+      a = i * sliceCount + j + offset;
+      b = i * sliceCount + j + 1 + offset;
+      c = (i + 1)* sliceCount + j + 1 + offset;
+      d = (i + 1)* sliceCount + j + offset;
+
+      uva = [j/detailX, i/detailY];
+      uvb = [(j + 1)/ detailX, i/detailY];
+      uvc = [(j + 1)/ detailX, (i + 1)/detailY];
+      uvd = [j/detailX, (i + 1)/detailY];
+
+      this.faces.push([a, b, d]);
+      this.uvs.push([uva, uvb, uvd]);
+
+      this.faces.push([b, c, d]);
+      this.uvs.push([uvb, uvc, uvd]);
+    }
+  }
+};
+
+/**
+ * compute faceNormals for a geometry
+ */
+p5.Geometry3D.prototype.computeFaceNormals = function(){
+
+  var cb = new p5.Vector();
+  var ab = new p5.Vector();
+
+  for (var f = 0; f < this.faces.length; f++){
+    var face = this.faces[f];
+    var vA = this.vertices[face[0]];
+    var vB = this.vertices[face[1]];
+    var vC = this.vertices[face[2]];
+
+    p5.Vector.sub(vC, vB, cb);
+    p5.Vector.sub(vA, vB, ab);
+
+    var normal = p5.Vector.cross(ab, cb);
+    normal.normalize();
+    normal.mult(-1);
+    this.faceNormals[f] = normal;
+  }
+
+};
+
+/**
+ * compute vertexNormals for a geometry
+ */
+p5.Geometry3D.prototype.computeVertexNormals = function (){
+
+  var v, f, face, faceNormal, vertices;
+  var vertexNormals = [];
+
+  vertices = new Array(this.vertices.length);
+  for (v = 0; v < this.vertices.length; v++) {
+    vertices[v] = new p5.Vector();
+  }
+
+  for (f = 0; f < this.faces.length; f++) {
+    face = this.faces[f];
+    faceNormal = this.faceNormals[f];
+
+    vertices[face[0]].add(faceNormal);
+    vertices[face[1]].add(faceNormal);
+    vertices[face[2]].add(faceNormal);
+  }
+
+  for (v = 0; v < this.vertices.length; v++) {
+    vertices[v].normalize();
+  }
+
+  for (f = 0; f < this.faces.length; f++) {
+    face = this.faces[f];
+    vertexNormals[f] = [];
+    vertexNormals[f][0]= vertices[face[0]].copy();
+    vertexNormals[f][1]= vertices[face[1]].copy();
+    vertexNormals[f][2]= vertices[face[2]].copy();
+  }
+
+  for (f = 0; f < this.faces.length; f++){
+    face = this.faces[f];
+    faceNormal = this.faceNormals[f];
+    this.vertexNormals[face[0]] = vertexNormals[f][0];
+    this.vertexNormals[face[1]] = vertexNormals[f][1];
+    this.vertexNormals[face[2]] = vertexNormals[f][2];
+  }
+
+};
+
+p5.Geometry3D.prototype.averageNormals = function() {
+
+  for(var i = 0; i <= this.detailY; i++){
+    var offset = this.detailX + 1;
+    var temp = p5.Vector
+      .add(this.vertexNormals[i*offset],
+        this.vertexNormals[i*offset + this.detailX]);
+    temp = p5.Vector.div(temp, 2);
+    this.vertexNormals[i*offset] = temp;
+    this.vertexNormals[i*offset + this.detailX] = temp;
+  }
+};
+
+p5.Geometry3D.prototype.averagePoleNormals = function() {
+
+  //average the north pole
+  var sum = new p5.Vector(0, 0, 0);
+  for(var i = 0; i < this.detailX; i++){
+    sum.add(this.vertexNormals[i]);
+  }
+  sum = p5.Vector.div(sum, this.detailX);
+
+  for(i = 0; i < this.detailX; i++){
+    this.vertexNormals[i] = sum;
+  }
+
+  //average the south pole
+  sum = new p5.Vector(0, 0, 0);
+  for(i = this.vertices.length - 1;
+    i > this.vertices.length - 1 - this.detailX; i--){
+    sum.add(this.vertexNormals[i]);
+  }
+  sum = p5.Vector.div(sum, this.detailX);
+
+  for(i = this.vertices.length - 1;
+    i > this.vertices.length - 1 - this.detailX; i--){
+    this.vertexNormals[i] = sum;
+  }
+};
+
+/**
+ * [generateUV description]
+ * @param  {Array} faces [description]
+ * @param  {Array} uvs   [description]
+ */
+p5.Geometry3D.prototype.generateUV = function(faces, uvs){
+
+  faces = flatten(faces);
+  uvs = flatten(uvs);
+  var arr = [];
+  faces.forEach(function(item, index){
+    arr[item] = uvs[index];
+  });
+  return flatten(arr);
+};
+
+
+/**
+ * generate an object containing information needed to create buffer
+ */
+p5.Geometry3D.prototype.generateObj = function(average, sphere){
+
+  this.computeFaceNormals();
+  this.computeVertexNormals();
+
+  if(average){
+    this.averageNormals();
+  }
+
+  if(sphere){
+    this.averagePoleNormals();
+  }
+
+  var obj = {
+    vertices: turnVectorArrayIntoNumberArray(this.vertices),
+    vertexNormals: turnVectorArrayIntoNumberArray(this.vertexNormals),
+    uvs: this.generateUV(this.faces, this.uvs),
+    faces: flatten(this.faces),
+    len: this.faces.length * 3
+  };
+  return obj;
+};
+
+/**
+ * turn a two dimensional array into one dimensional array
+ * @param  {Array} arr 2-dimensional array
+ * @return {Array}     1-dimensional array
+ * [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6]
+ */
+function flatten(arr){
+  return arr.reduce(function(a, b){
+    return a.concat(b);
+  });
+}
+
+/**
+ * turn an array of Vector into a one dimensional array of numbers
+ * @param  {Array} arr  an array of p5.Vector
+ * @return {Array]}     a one dimensional array of numbers
+ * [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] ->
+ * [1, 2, 3, 4, 5, 6]
+ */
+function turnVectorArrayIntoNumberArray(arr){
+  return flatten(arr.map(function(item){
+    return [item.x, item.y, item.z];
+  }));
+}
+
+module.exports = p5.Geometry3D;
+},{"../core/core":48}],35:[function(_dereq_,module,exports){
+/**
+* @requires constants
+* @todo see methods below needing further implementation.
+* future consideration: implement SIMD optimizations
+* when browser compatibility becomes available
+* https://developer.mozilla.org/en-US/docs/Web/JavaScript/
+*   Reference/Global_Objects/SIMD
+*/
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('../math/polargeometry');
+var constants = _dereq_('../core/constants');
+var GLMAT_ARRAY_TYPE = (
+    typeof Float32Array !== 'undefined') ?
+  Float32Array : Array;
+
+/**
+ * A class to describe a 4x4 matrix
+ * for model and view matrix manipulation in the p5js webgl renderer.
+ * class p5.Matrix
+ * @constructor
+ * @param {Array} [mat4] array literal of our 4x4 matrix
+ */
+p5.Matrix = function() {
+  // This is how it comes in with createMatrix()
+  if(arguments[0] instanceof p5) {
+    // save reference to p5 if passed in
+    this.p5 = arguments[0];
+    this.mat4  = arguments[1] || new GLMAT_ARRAY_TYPE([
+      1, 0, 0, 0,
+      0, 1, 0, 0,
+      0, 0, 1, 0,
+      0, 0, 0, 1
+    ]);
+  // This is what we'll get with new p5.Matrix()
+  // a mat4 identity matrix
+  } else {
+    this.mat4 = arguments[0] || new GLMAT_ARRAY_TYPE([
+      1, 0, 0, 0,
+      0, 1, 0, 0,
+      0, 0, 1, 0,
+      0, 0, 0, 1
+    ]);
+  }
+  return this;
+};
+
+/**
+ * Sets the x, y, and z component of the vector using two or three separate
+ * variables, the data from a p5.Matrix, or the values from a float array.
+ *
+ * @param {p5.Matrix|Array} [inMatrix] the input p5.Matrix or
+ *                                     an Array of length 16
+ */
+p5.Matrix.prototype.set = function (inMatrix) {
+  if (inMatrix instanceof p5.Matrix) {
+    this.mat4 = inMatrix.mat4;
+    return this;
+  }
+  else if (inMatrix instanceof GLMAT_ARRAY_TYPE) {
+    this.mat4 = inMatrix;
+    return this;
+  }
+  return this;
+};
+
+/**
+ * Gets a copy of the vector, returns a p5.Matrix object.
+ *
+ * @return {p5.Matrix} the copy of the p5.Matrix object
+ */
+p5.Matrix.prototype.get = function () {
+  return new p5.Matrix(this.mat4);
+};
+
+/**
+ * return a copy of a matrix
+ * @return {p5.Matrix}   the result matrix
+ */
+p5.Matrix.prototype.copy = function(){
+  var copied = new p5.Matrix();
+  copied.mat4[0] = this.mat4[0];
+  copied.mat4[1] = this.mat4[1];
+  copied.mat4[2] = this.mat4[2];
+  copied.mat4[3] = this.mat4[3];
+  copied.mat4[4] = this.mat4[4];
+  copied.mat4[5] = this.mat4[5];
+  copied.mat4[6] = this.mat4[6];
+  copied.mat4[7] = this.mat4[7];
+  copied.mat4[8] = this.mat4[8];
+  copied.mat4[9] = this.mat4[9];
+  copied.mat4[10] = this.mat4[10];
+  copied.mat4[11] = this.mat4[11];
+  copied.mat4[12] = this.mat4[12];
+  copied.mat4[13] = this.mat4[13];
+  copied.mat4[14] = this.mat4[14];
+  copied.mat4[15] = this.mat4[15];
+  return copied;
+};
+
+/**
+ * return an identity matrix
+ * @return {p5.Matrix}   the result matrix
+ */
+p5.Matrix.identity = function(){
+  return new p5.Matrix();
+};
+
+/**
+ * transpose according to a given matrix
+ * @param  {p5.Matrix | Typed Array} a  the matrix to be based on to transpose
+ * @return {p5.Matrix}                  this
+ */
+p5.Matrix.prototype.transpose = function(a){
+  var a01, a02, a03, a12, a13, a23;
+  if(a instanceof p5.Matrix){
+    a01 = a.mat4[1];
+    a02 = a.mat4[2];
+    a03 = a.mat4[3];
+    a12 = a.mat4[6];
+    a13 = a.mat4[7];
+    a23 = a.mat4[11];
+
+    this.mat4[0] = a.mat4[0];
+    this.mat4[1] = a.mat4[4];
+    this.mat4[2] = a.mat4[8];
+    this.mat4[3] = a.mat4[12];
+    this.mat4[4] = a01;
+    this.mat4[5] = a.mat4[5];
+    this.mat4[6] = a.mat4[9];
+    this.mat4[7] = a.mat4[13];
+    this.mat4[8] = a02;
+    this.mat4[9] = a12;
+    this.mat4[10] = a.mat4[10];
+    this.mat4[11] = a.mat4[14];
+    this.mat4[12] = a03;
+    this.mat4[13] = a13;
+    this.mat4[14] = a23;
+    this.mat4[15] = a.mat4[15];
+
+  }else if(a instanceof GLMAT_ARRAY_TYPE){
+    a01 = a[1];
+    a02 = a[2];
+    a03 = a[3];
+    a12 = a[6];
+    a13 = a[7];
+    a23 = a[11];
+
+    this.mat4[0] = a[0];
+    this.mat4[1] = a[4];
+    this.mat4[2] = a[8];
+    this.mat4[3] = a[12];
+    this.mat4[4] = a01;
+    this.mat4[5] = a[5];
+    this.mat4[6] = a[9];
+    this.mat4[7] = a[13];
+    this.mat4[8] = a02;
+    this.mat4[9] = a12;
+    this.mat4[10] = a[10];
+    this.mat4[11] = a[14];
+    this.mat4[12] = a03;
+    this.mat4[13] = a13;
+    this.mat4[14] = a23;
+    this.mat4[15] = a[15];
+  }
+  return this;
+};
+
+/**
+ * invert  matrix according to a give matrix
+ * @param  {p5.Matrix or Typed Array} a   the matrix to be based on to invert
+ * @return {p5.Matrix}                    this
+ */
+p5.Matrix.prototype.invert = function(a){
+  var a00, a01, a02, a03, a10, a11, a12, a13,
+  a20, a21, a22, a23, a30, a31, a32, a33;
+  if(a instanceof p5.Matrix){
+    a00 = a.mat4[0];
+    a01 = a.mat4[1];
+    a02 = a.mat4[2];
+    a03 = a.mat4[3];
+    a10 = a.mat4[4];
+    a11 = a.mat4[5];
+    a12 = a.mat4[6];
+    a13 = a.mat4[7];
+    a20 = a.mat4[8];
+    a21 = a.mat4[9];
+    a22 = a.mat4[10];
+    a23 = a.mat4[11];
+    a30 = a.mat4[12];
+    a31 = a.mat4[13];
+    a32 = a.mat4[14];
+    a33 = a.mat4[15];
+  }else if(a instanceof GLMAT_ARRAY_TYPE){
+    a00 = a[0];
+    a01 = a[1];
+    a02 = a[2];
+    a03 = a[3];
+    a10 = a[4];
+    a11 = a[5];
+    a12 = a[6];
+    a13 = a[7];
+    a20 = a[8];
+    a21 = a[9];
+    a22 = a[10];
+    a23 = a[11];
+    a30 = a[12];
+    a31 = a[13];
+    a32 = a[14];
+    a33 = a[15];
+  }
+  var b00 = a00 * a11 - a01 * a10,
+  b01 = a00 * a12 - a02 * a10,
+  b02 = a00 * a13 - a03 * a10,
+  b03 = a01 * a12 - a02 * a11,
+  b04 = a01 * a13 - a03 * a11,
+  b05 = a02 * a13 - a03 * a12,
+  b06 = a20 * a31 - a21 * a30,
+  b07 = a20 * a32 - a22 * a30,
+  b08 = a20 * a33 - a23 * a30,
+  b09 = a21 * a32 - a22 * a31,
+  b10 = a21 * a33 - a23 * a31,
+  b11 = a22 * a33 - a23 * a32,
+
+  // Calculate the determinant
+  det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 -
+  b04 * b07 + b05 * b06;
+
+  if (!det) {
+    return null;
+  }
+  det = 1.0 / det;
+
+  this.mat4[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+  this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+  this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+  this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
+  this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+  this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+  this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+  this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
+  this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+  this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+  this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+  this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
+  this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
+  this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
+  this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
+  this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
+
+  return this;
+};
+
+/**
+ * inspired by Toji's mat4 determinant
+ * @return {Number} Determinant of our 4x4 matrix
+ */
+p5.Matrix.prototype.determinant = function(){
+  var d00 = (this.mat4[0] * this.mat4[5]) - (this.mat4[1] * this.mat4[4]),
+    d01 = (this.mat4[0] * this.mat4[6]) - (this.mat4[2] * this.mat4[4]),
+    d02 = (this.mat4[0] * this.mat4[7]) - (this.mat4[3] * this.mat4[4]),
+    d03 = (this.mat4[1] * this.mat4[6]) - (this.mat4[2] * this.mat4[5]),
+    d04 = (this.mat4[1] * this.mat4[7]) - (this.mat4[3] * this.mat4[5]),
+    d05 = (this.mat4[2] * this.mat4[7]) - (this.mat4[3] * this.mat4[6]),
+    d06 = (this.mat4[8] * this.mat4[13]) - (this.mat4[9] * this.mat4[12]),
+    d07 = (this.mat4[8] * this.mat4[14]) - (this.mat4[10] * this.mat4[12]),
+    d08 = (this.mat4[8] * this.mat4[15]) - (this.mat4[11] * this.mat4[12]),
+    d09 = (this.mat4[9] * this.mat4[14]) - (this.mat4[10] * this.mat4[13]),
+    d10 = (this.mat4[9] * this.mat4[15]) - (this.mat4[11] * this.mat4[13]),
+    d11 = (this.mat4[10] * this.mat4[15]) - (this.mat4[11] * this.mat4[14]);
+
+  // Calculate the determinant
+  return d00 * d11 - d01 * d10 + d02 * d09 +
+    d03 * d08 - d04 * d07 + d05 * d06;
+};
+
+/**
+ * multiply two mat4s
+ * @param {p5.Matrix | Array}  multMatrix The matrix we want to multiply by
+ * @return {p5.Matrix}         this
+ */
+p5.Matrix.prototype.mult = function(multMatrix){
+  var _dest = new GLMAT_ARRAY_TYPE(16);
+  var _src = new GLMAT_ARRAY_TYPE(16);
+
+  if(multMatrix instanceof p5.Matrix) {
+    _src = multMatrix.mat4;
+  }
+  else if(multMatrix instanceof GLMAT_ARRAY_TYPE){
+    _src = multMatrix;
+  }
+
+  // each row is used for the multiplier
+  var b0  = this.mat4[0], b1 = this.mat4[1],
+    b2 = this.mat4[2], b3 = this.mat4[3];
+  _dest[0] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+  _dest[1] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+  _dest[2] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+  _dest[3] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+  b0 = this.mat4[4];
+  b1 = this.mat4[5];
+  b2 = this.mat4[6];
+  b3 = this.mat4[7];
+  _dest[4] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+  _dest[5] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+  _dest[6] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+  _dest[7] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+  b0 = this.mat4[8];
+  b1 = this.mat4[9];
+  b2 = this.mat4[10];
+  b3 = this.mat4[11];
+  _dest[8] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+  _dest[9] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+  _dest[10] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+  _dest[11] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+  b0 = this.mat4[12];
+  b1 = this.mat4[13];
+  b2 = this.mat4[14];
+  b3 = this.mat4[15];
+  _dest[12] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+  _dest[13] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+  _dest[14] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+  _dest[15] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+  this.mat4 = _dest;
+
+  return this;
+};
+
+/**
+ * scales a p5.Matrix by scalars or a vector
+ * @param  {p5.Vector | Array }
+ *                      vector to scale by
+ * @return {p5.Matrix}  this
+ */
+p5.Matrix.prototype.scale = function() {
+  var x,y,z;
+  var args = new Array(arguments.length);
+  for(var i = 0; i < args.length; i++) {
+    args[i] = arguments[i];
+  }
+  //if our 1st arg is a type p5.Vector
+  if (args[0] instanceof p5.Vector){
+    x = args[0].x;
+    y = args[0].y;
+    z = args[0].z;
+  }
+  //otherwise if it's an array
+  else if (args[0] instanceof Array){
+    x = args[0][0];
+    y = args[0][1];
+    z = args[0][2];
+  }
+  var _dest = new GLMAT_ARRAY_TYPE(16);
+  _dest[0] = this.mat4[0] * x;
+  _dest[1] = this.mat4[1] * x;
+  _dest[2] = this.mat4[2] * x;
+  _dest[3] = this.mat4[3] * x;
+  _dest[4] = this.mat4[4] * y;
+  _dest[5] = this.mat4[5] * y;
+  _dest[6] = this.mat4[6] * y;
+  _dest[7] = this.mat4[7] * y;
+  _dest[8] = this.mat4[8] * z;
+  _dest[9] = this.mat4[9] * z;
+  _dest[10] = this.mat4[10] * z;
+  _dest[11] = this.mat4[11] * z;
+  _dest[12] = this.mat4[12];
+  _dest[13] = this.mat4[13];
+  _dest[14] = this.mat4[14];
+  _dest[15] = this.mat4[15];
+
+  this.mat4 = _dest;
+  return this;
+};
+
+/**
+ * rotate our Matrix around an axis by the given angle.
+ * @param  {Number} a The angle of rotation in radians
+ * @param  {p5.Vector | Array} axis  the axis(es) to rotate around
+ * @return {p5.Matrix}                    this
+ * inspired by Toji's gl-matrix lib, mat4 rotation
+ */
+p5.Matrix.prototype.rotate = function(a, axis){
+  var x, y, z, _a, len;
+
+  if (this.p5) {
+    if (this.p5._angleMode === constants.DEGREES) {
+      _a = polarGeometry.degreesToRadians(a);
+    }
+  }
+  else {
+    _a = a;
+  }
+  if (axis instanceof p5.Vector) {
+    x = axis.x;
+    y = axis.y;
+    z = axis.z;
+  }
+  else if (axis instanceof Array) {
+    x = axis[0];
+    y = axis[1];
+    z = axis[2];
+  }
+
+  len = Math.sqrt(x * x + y * y + z * z);
+  x *= (1/len);
+  y *= (1/len);
+  z *= (1/len);
+
+  var a00 = this.mat4[0];
+  var a01 = this.mat4[1];
+  var a02 = this.mat4[2];
+  var a03 = this.mat4[3];
+  var a10 = this.mat4[4];
+  var a11 = this.mat4[5];
+  var a12 = this.mat4[6];
+  var a13 = this.mat4[7];
+  var a20 = this.mat4[8];
+  var a21 = this.mat4[9];
+  var a22 = this.mat4[10];
+  var a23 = this.mat4[11];
+
+  //sin,cos, and tan of respective angle
+  var sA = Math.sin(_a);
+  var cA = Math.cos(_a);
+  var tA = 1 - cA;
+  // Construct the elements of the rotation matrix
+  var b00 = x * x * tA + cA;
+  var b01 = y * x * tA + z * sA;
+  var b02 = z * x * tA - y * sA;
+  var b10 = x * y * tA - z * sA;
+  var b11 = y * y * tA + cA;
+  var b12 = z * y * tA + x * sA;
+  var b20 = x * z * tA + y * sA;
+  var b21 = y * z * tA - x * sA;
+  var b22 = z * z * tA + cA;
+
+  // rotation-specific matrix multiplication
+  this.mat4[0] = a00 * b00 + a10 * b01 + a20 * b02;
+  this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02;
+  this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02;
+  this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02;
+  this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12;
+  this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12;
+  this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12;
+  this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12;
+  this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22;
+  this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22;
+  this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22;
+  this.mat4[11] = a03 * b20 + a13 * b21 + a23 * b22;
+
+  return this;
+};
+
+/**
+ * @todo  finish implementing this method!
+ * translates
+ * @param  {Array} v vector to translate by
+ * @return {p5.Matrix}                    this
+ */
+p5.Matrix.prototype.translate = function(v){
+  var x = v[0],
+    y = v[1],
+    z = v[2] || 0;
+  this.mat4[12] =
+    this.mat4[0] * x +this.mat4[4] * y +this.mat4[8] * z +this.mat4[12];
+  this.mat4[13] =
+    this.mat4[1] * x +this.mat4[5] * y +this.mat4[9] * z +this.mat4[13];
+  this.mat4[14] =
+    this.mat4[2] * x +this.mat4[6] * y +this.mat4[10] * z +this.mat4[14];
+  this.mat4[15] =
+    this.mat4[3] * x +this.mat4[7] * y +this.mat4[11] * z +this.mat4[15];
+};
+
+p5.Matrix.prototype.rotateX = function(a){
+  this.rotate(a, [1,0,0]);
+};
+p5.Matrix.prototype.rotateY = function(a){
+  this.rotate(a, [0,1,0]);
+};
+p5.Matrix.prototype.rotateZ = function(a){
+  this.rotate(a, [0,0,1]);
+};
+
+/**
+ * sets the perspective matrix
+ * @param  {Number} fovy   [description]
+ * @param  {Number} aspect [description]
+ * @param  {Number} near   near clipping plane
+ * @param  {Number} far    far clipping plane
+ * @return {void}
+ */
+p5.Matrix.prototype.perspective = function(fovy,aspect,near,far){
+
+  var f = 1.0 / Math.tan(fovy / 2),
+    nf = 1 / (near - far);
+
+  this.mat4[0] = f / aspect;
+  this.mat4[1] = 0;
+  this.mat4[2] = 0;
+  this.mat4[3] = 0;
+  this.mat4[4] = 0;
+  this.mat4[5] = f;
+  this.mat4[6] = 0;
+  this.mat4[7] = 0;
+  this.mat4[8] = 0;
+  this.mat4[9] = 0;
+  this.mat4[10] = (far + near) * nf;
+  this.mat4[11] = -1;
+  this.mat4[12] = 0;
+  this.mat4[13] = 0;
+  this.mat4[14] = (2 * far * near) * nf;
+  this.mat4[15] = 0;
+
+  return this;
+
+};
+
+/**
+ * sets the ortho matrix
+ * @param  {Number} left   [description]
+ * @param  {Number} right  [description]
+ * @param  {Number} bottom [description]
+ * @param  {Number} top    [description]
+ * @param  {Number} near   near clipping plane
+ * @param  {Number} far    far clipping plane
+ * @return {void}
+ */
+p5.Matrix.prototype.ortho = function(left,right,bottom,top,near,far){
+
+  var lr = 1 / (left - right),
+    bt = 1 / (bottom - top),
+    nf = 1 / (near - far);
+  this.mat4[0] = -2 * lr;
+  this.mat4[1] = 0;
+  this.mat4[2] = 0;
+  this.mat4[3] = 0;
+  this.mat4[4] = 0;
+  this.mat4[5] = -2 * bt;
+  this.mat4[6] = 0;
+  this.mat4[7] = 0;
+  this.mat4[8] = 0;
+  this.mat4[9] = 0;
+  this.mat4[10] = 2 * nf;
+  this.mat4[11] = 0;
+  this.mat4[12] = (left + right) * lr;
+  this.mat4[13] = (top + bottom) * bt;
+  this.mat4[14] = (far + near) * nf;
+  this.mat4[15] = 1;
+
+  return this;
+};
+
+/**
+ * PRIVATE
+ */
+// matrix methods adapted from:
+// https://developer.mozilla.org/en-US/docs/Web/WebGL/
+// gluPerspective
+//
+// function _makePerspective(fovy, aspect, znear, zfar){
+//    var ymax = znear * Math.tan(fovy * Math.PI / 360.0);
+//    var ymin = -ymax;
+//    var xmin = ymin * aspect;
+//    var xmax = ymax * aspect;
+//    return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar);
+//  }
+
+////
+//// glFrustum
+////
+//function _makeFrustum(left, right, bottom, top, znear, zfar){
+//  var X = 2*znear/(right-left);
+//  var Y = 2*znear/(top-bottom);
+//  var A = (right+left)/(right-left);
+//  var B = (top+bottom)/(top-bottom);
+//  var C = -(zfar+znear)/(zfar-znear);
+//  var D = -2*zfar*znear/(zfar-znear);
+//  var frustrumMatrix =[
+//  X, 0, A, 0,
+//  0, Y, B, 0,
+//  0, 0, C, D,
+//  0, 0, -1, 0
+//];
+//return frustrumMatrix;
+// }
+
+// function _setMVPMatrices(){
+////an identity matrix
+////@TODO use the p5.Matrix class to abstract away our MV matrices and
+///other math
+//var _mvMatrix =
+//[
+//  1.0,0.0,0.0,0.0,
+//  0.0,1.0,0.0,0.0,
+//  0.0,0.0,1.0,0.0,
+//  0.0,0.0,0.0,1.0
+//];
+
+module.exports = p5.Matrix;
+},{"../core/constants":47,"../core/core":48,"../math/polargeometry":77}],36:[function(_dereq_,module,exports){
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var shader = _dereq_('./shader');
+_dereq_('../core/p5.Renderer');
+_dereq_('./p5.Matrix');
+var uMVMatrixStack = [];
+var RESOLUTION = 1000;
+
+//@TODO should probably implement an override for these attributes
+var attributes = {
+  alpha: true,
+  depth: true,
+  stencil: true,
+  antialias: false,
+  premultipliedAlpha: false,
+  preserveDrawingBuffer: false
+};
+
+/**
+ * 3D graphics class.  Can also be used as an off-screen graphics buffer.
+ * A p5.Renderer3D object can be constructed
+ *
+ */
+p5.Renderer3D = function(elt, pInst, isMainCanvas) {
+  p5.Renderer.call(this, elt, pInst, isMainCanvas);
+
+  try {
+    this.drawingContext = this.canvas.getContext('webgl', attributes) ||
+      this.canvas.getContext('experimental-webgl', attributes);
+    if (this.drawingContext === null) {
+      throw new Error('Error creating webgl context');
+    } else {
+      console.log('p5.Renderer3D: enabled webgl context');
+    }
+  } catch (er) {
+    throw new Error(er);
+  }
+
+  this.isP3D = true; //lets us know we're in 3d mode
+  this.GL = this.drawingContext;
+  var gl = this.GL;
+  gl.clearColor(1.0, 1.0, 1.0, 1.0); //background is initialized white
+  gl.clearDepth(1);
+  gl.enable(gl.DEPTH_TEST);
+  gl.depthFunc(gl.LEQUAL);
+  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+  this._init();
+  return this;
+};
+
+p5.Renderer3D.prototype = Object.create(p5.Renderer.prototype);
+
+p5.Renderer3D.prototype._applyDefaults = function() {
+  return this;
+};
+
+//////////////////////////////////////////////
+// Setting
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype._init = function(first_argument) {
+  var gl = this.GL;
+  //for our default matrices
+  this.initMatrix();
+  this.initHash();
+  //for immedidate mode
+  this.verticeStack = [];
+  this.verticeBuffer = gl.createBuffer();
+  this.colorBuffer = gl.createBuffer();
+  //for camera
+  this._setCamera = false;
+  //for counting lights
+  this.ambientLightCount = 0;
+  this.directionalLightCount = 0;
+  this.pointLightCount = 0;
+};
+
+p5.Renderer3D.prototype._update = function() {
+  this.resetMatrix();
+  this.translate(0, 0, -800);
+  this.ambientLightCount = 0;
+  this.directionalLightCount = 0;
+  this.pointLightCount = 0;
+  this.verticeStack = [];
+};
+
+/**
+ * [resize description]
+ * @param  {[type]} w [description]
+ * @param  {[tyoe]} h [description]
+ * @return {[type]}   [description]
+ */
+p5.Renderer3D.prototype.resize = function(w,h) {
+  var gl = this.GL;
+  p5.Renderer.prototype.resize.call(this, w, h);
+  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+};
+
+/**
+ * [background description]
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.background = function() {
+  var gl = this.GL;
+  var _col = this._pInst.color.apply(this._pInst, arguments);
+  // gl.clearColor(0.0,0.0,0.0,1.0);
+  var _r = (_col.levels[0]) / 255;
+  var _g = (_col.levels[1]) / 255;
+  var _b = (_col.levels[2]) / 255;
+  var _a = (_col.levels[3]) / 255;
+  gl.clearColor(_r, _g, _b, _a);
+  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+};
+
+//@TODO implement this
+// p5.Renderer3D.prototype.clear = function() {
+//@TODO
+// };
+
+//////////////////////////////////////////////
+// SHADER
+//////////////////////////////////////////////
+
+/**
+ * [initShaders description]
+ * @param  {[type]} vertId [description]
+ * @param  {[type]} fragId [description]
+ * @return {[type]}        [description]
+ */
+p5.Renderer3D.prototype.initShaders = function(vertId, fragId, immediateMode) {
+  var gl = this.GL;
+  //set up our default shaders by:
+  // 1. create the shader,
+  // 2. load the shader source,
+  // 3. compile the shader
+  var _vertShader = gl.createShader(gl.VERTEX_SHADER);
+  //load in our default vertex shader
+  gl.shaderSource(_vertShader, shader[vertId]);
+  gl.compileShader(_vertShader);
+  // if our vertex shader failed compilation?
+  if (!gl.getShaderParameter(_vertShader, gl.COMPILE_STATUS)) {
+    alert('Yikes! An error occurred compiling the shaders:' +
+      gl.getShaderInfoLog(_vertShader));
+    return null;
+  }
+
+  var _fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+  //load in our material frag shader
+  gl.shaderSource(_fragShader, shader[fragId]);
+  gl.compileShader(_fragShader);
+  // if our frag shader failed compilation?
+  if (!gl.getShaderParameter(_fragShader, gl.COMPILE_STATUS)) {
+    alert('Darn! An error occurred compiling the shaders:' +
+      gl.getShaderInfoLog(_fragShader));
+    return null;
+  }
+
+  var shaderProgram = gl.createProgram();
+  gl.attachShader(shaderProgram, _vertShader);
+  gl.attachShader(shaderProgram, _fragShader);
+  gl.linkProgram(shaderProgram);
+  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+    alert('Snap! Error linking shader program');
+  }
+  //END SHADERS SETUP
+
+  this._getLocation(shaderProgram, immediateMode);
+
+  return shaderProgram;
+};
+
+p5.Renderer3D.prototype._getLocation = function(shaderProgram, immediateMode) {
+  var gl = this.GL;
+  gl.useProgram(shaderProgram);
+  shaderProgram.uResolution =
+    gl.getUniformLocation(shaderProgram, 'uResolution');
+  gl.uniform1f(shaderProgram.uResolution, RESOLUTION);
+
+  //vertex position Attribute
+  shaderProgram.vertexPositionAttribute =
+    gl.getAttribLocation(shaderProgram, 'aPosition');
+  gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
+
+  //projection Matrix uniform
+  shaderProgram.uPMatrixUniform =
+    gl.getUniformLocation(shaderProgram, 'uProjectionMatrix');
+  //model view Matrix uniform
+  shaderProgram.uMVMatrixUniform =
+    gl.getUniformLocation(shaderProgram, 'uModelViewMatrix');
+
+  //@TODO: figure out a better way instead of if statement
+  if(immediateMode === undefined){
+    //vertex normal Attribute
+    shaderProgram.vertexNormalAttribute =
+      gl.getAttribLocation(shaderProgram, 'aNormal');
+    gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);
+
+    //normal Matrix uniform
+    shaderProgram.uNMatrixUniform =
+    gl.getUniformLocation(shaderProgram, 'uNormalMatrix');
+
+    //texture coordinate Attribute
+    shaderProgram.textureCoordAttribute =
+      gl.getAttribLocation(shaderProgram, 'aTexCoord');
+    gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
+
+    shaderProgram.samplerUniform =
+    gl.getUniformLocation(shaderProgram, 'uSampler');
+  }
+};
+
+p5.Renderer3D.prototype.setMatrixUniforms = function(shaderKey) {
+  var gl = this.GL;
+  var shaderProgram = this.mHash[shaderKey];
+
+  gl.useProgram(shaderProgram);
+
+  gl.uniformMatrix4fv(
+    shaderProgram.uPMatrixUniform,
+    false, this.uPMatrix.mat4);
+
+  gl.uniformMatrix4fv(
+    shaderProgram.uMVMatrixUniform,
+    false, this.uMVMatrix.mat4);
+
+  this.uNMatrix = new p5.Matrix();
+  this.uNMatrix.invert(this.uMVMatrix);
+  this.uNMatrix.transpose(this.uNMatrix);
+
+  gl.uniformMatrix4fv(
+    shaderProgram.uNMatrixUniform,
+    false, this.uNMatrix.mat4);
+};
+//////////////////////////////////////////////
+// GET CURRENT | for shader and color
+//////////////////////////////////////////////
+p5.Renderer3D.prototype._getShader = function(vertId, fragId, immediateMode) {
+  var mId = vertId+ '|' + fragId;
+  //create it and put it into hashTable
+  if(!this.materialInHash(mId)){
+    var shaderProgram = this.initShaders(vertId, fragId, immediateMode);
+    this.mHash[mId] = shaderProgram;
+  }
+  this.curShaderId = mId;
+
+  return this.mHash[this.curShaderId];
+};
+
+p5.Renderer3D.prototype._getCurShaderId = function(){
+  //if it's not defined yet
+  if(this.curShaderId === undefined){
+    //default shader: normalMaterial()
+    var mId = 'normalVert|normalFrag';
+    var shaderProgram = this.initShaders('normalVert', 'normalFrag');
+    this.mHash[mId] = shaderProgram;
+    this.curShaderId = mId;
+  }
+
+  return this.curShaderId;
+};
+
+p5.Renderer3D.prototype._getCurColor = function() {
+  //default color: gray
+  if(this.curColor === undefined) {
+    this.curColor = [0.5, 0.5, 0.5, 1.0];
+  }
+  return this.curColor;
+};
+
+//////////////////////////////////////////////
+// HASH | for material and geometry
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype.initHash = function(){
+  this.gHash = {};
+  this.mHash = {};
+};
+
+p5.Renderer3D.prototype.geometryInHash = function(gId){
+  return this.gHash[gId] !== undefined;
+};
+
+p5.Renderer3D.prototype.materialInHash = function(mId){
+  return this.mHash[mId] !== undefined;
+};
+
+//////////////////////////////////////////////
+// MATRIX
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype.initMatrix = function(){
+  this.uMVMatrix = new p5.Matrix();
+  this.uPMatrix  = new p5.Matrix();
+  this.uNMatrix = new p5.Matrix();
+};
+
+p5.Renderer3D.prototype.resetMatrix = function() {
+  this.uMVMatrix = p5.Matrix.identity();
+  //this.uPMatrix = p5.Matrix.identity();
+};
+
+//detect if user didn't set the camera
+//then call this function below
+p5.Renderer3D.prototype._setDefaultCamera = function(){
+  if(!this._setCamera){
+    var _w = this.width;
+    var _h = this.height;
+    this.uPMatrix = p5.Matrix.identity();
+    this.uPMatrix.perspective(60 / 180 * Math.PI, _w / _h, 0.1, 100);
+    this._setCamera = true;
+  }
+};
+
+/**
+ * [translate description]
+ * @param  {[type]} x [description]
+ * @param  {[type]} y [description]
+ * @param  {[type]} z [description]
+ * @return {[type]}   [description]
+ * @todo implement handle for components or vector as args
+ */
+p5.Renderer3D.prototype.translate = function(x, y, z) {
+  //@TODO: figure out how to fit the resolution
+  x = x / RESOLUTION;
+  y = -y / RESOLUTION;
+  z = z / RESOLUTION;
+  this.uMVMatrix.translate([x,y,z]);
+  return this;
+};
+
+/**
+ * Scales the Model View Matrix by a vector
+ * @param  {Number | p5.Vector | Array} x [description]
+ * @param  {Number} [y] y-axis scalar
+ * @param  {Number} [z] z-axis scalar
+ * @return {this}   [description]
+ */
+p5.Renderer3D.prototype.scale = function(x,y,z) {
+  this.uMVMatrix.scale([x,y,z]);
+  return this;
+};
+
+/**
+ * [rotate description]
+ * @param  {Number} rad  angle in radians
+ * @param  {p5.Vector | Array} axis axis to rotate around
+ * @return {p5.Renderer3D}      [description]
+ */
+p5.Renderer3D.prototype.rotate = function(rad, axis){
+  this.uMVMatrix.rotate(rad, axis);
+  return this;
+};
+
+/**
+ * [rotateX description]
+ * @param  {Number} rad radians to rotate
+ * @return {[type]}     [description]
+ */
+p5.Renderer3D.prototype.rotateX = function(rad) {
+  this.uMVMatrix.rotateX(rad);
+  return this;
+};
+
+/**
+ * [rotateY description]
+ * @param  {Number} rad rad radians to rotate
+ * @return {[type]}     [description]
+ */
+p5.Renderer3D.prototype.rotateY = function(rad) {
+  this.uMVMatrix.rotateY(rad);
+  return this;
+};
+
+/**
+ * [rotateZ description]
+ * @param  {Number} rad rad radians to rotate
+ * @return {[type]}     [description]
+ */
+p5.Renderer3D.prototype.rotateZ = function(rad) {
+  this.uMVMatrix.rotateZ(rad);
+  return this;
+};
+
+/**
+ * pushes a copy of the model view matrix onto the
+ * MV Matrix stack.
+ * NOTE to self: could probably make this more readable
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.push = function() {
+  uMVMatrixStack.push(this.uMVMatrix.copy());
+};
+
+/**
+ * [pop description]
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.pop = function() {
+  if (uMVMatrixStack.length === 0) {
+    throw new Error('Invalid popMatrix!');
+  }
+  this.uMVMatrix = uMVMatrixStack.pop();
+};
+
+module.exports = p5.Renderer3D;
+
+},{"../core/core":48,"../core/p5.Renderer":54,"./p5.Matrix":35,"./shader":38}],37:[function(_dereq_,module,exports){
+//retained mode is used by rendering 3d_primitives
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var hashCount = 0;
+
+/**
+ * createBuffer
+ * @param  {String} gId  key of the geometry object
+ * @param  {Array}  arr  array holding bject containing geometry information
+ */
+p5.Renderer3D.prototype.createBuffer = function(gId, arr) {
+
+  hashCount ++;
+  if(hashCount > 1000){
+    var key = Object.keys(this.gHash)[0];
+    delete this.gHash[key];
+    hashCount --;
+  }
+
+  var gl = this.GL;
+  this.gHash[gId] = {};
+  this.gHash[gId].len = [];
+  this.gHash[gId].vertexBuffer = [];
+  this.gHash[gId].normalBuffer = [];
+  this.gHash[gId].uvBuffer = [];
+  this.gHash[gId].indexBuffer =[];
+
+  arr.forEach(function(obj){
+    this.gHash[gId].len.push(obj.len);
+    this.gHash[gId].vertexBuffer.push(gl.createBuffer());
+    this.gHash[gId].normalBuffer.push(gl.createBuffer());
+    this.gHash[gId].uvBuffer.push(gl.createBuffer());
+    this.gHash[gId].indexBuffer.push(gl.createBuffer());
+  }.bind(this));
+};
+
+/**
+ * initBuffer description
+ * @param  {String} gId    key of the geometry object
+ * @param  {Array}  arr    array holding bject containing geometry information
+ */
+p5.Renderer3D.prototype.initBuffer = function(gId, arr) {
+  this._setDefaultCamera();
+  var gl = this.GL;
+  this.createBuffer(gId, arr);
+
+  var shaderProgram = this.mHash[this._getCurShaderId()];
+
+  arr.forEach(function(obj, i){
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer[i]);
+    gl.bufferData(
+      gl.ARRAY_BUFFER, new Float32Array(obj.vertices), gl.STATIC_DRAW);
+    gl.vertexAttribPointer(
+      shaderProgram.vertexPositionAttribute,
+      3, gl.FLOAT, false, 0, 0);
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer[i]);
+    gl.bufferData(
+      gl.ARRAY_BUFFER, new Float32Array(obj.vertexNormals), gl.STATIC_DRAW);
+    gl.vertexAttribPointer(
+      shaderProgram.vertexNormalAttribute,
+      3, gl.FLOAT, false, 0, 0);
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer[i]);
+    gl.bufferData(
+      gl.ARRAY_BUFFER, new Float32Array(obj.uvs), gl.STATIC_DRAW);
+    gl.vertexAttribPointer(
+      shaderProgram.textureCoordAttribute,
+      2, gl.FLOAT, false, 0, 0);
+
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer[i]);
+    gl.bufferData
+     (gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(obj.faces), gl.STATIC_DRAW);
+  }.bind(this));
+};
+
+/**
+ * drawBuffer
+ * @param  {String} gId     key of the geometery object
+ */
+p5.Renderer3D.prototype.drawBuffer = function(gId) {
+  this._setDefaultCamera();
+  var gl = this.GL;
+  var shaderKey = this._getCurShaderId();
+  var shaderProgram = this.mHash[shaderKey];
+
+  this.gHash[gId].len.forEach(function(d, i){
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer[i]);
+    gl.vertexAttribPointer(
+      shaderProgram.vertexPositionAttribute,
+      3, gl.FLOAT, false, 0, 0);
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer[i]);
+    gl.vertexAttribPointer(
+      shaderProgram.vertexNormalAttribute,
+      3, gl.FLOAT, false, 0, 0);
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer[i]);
+    gl.vertexAttribPointer(
+      shaderProgram.textureCoordAttribute,
+      2, gl.FLOAT, false, 0, 0);
+
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer[i]);
+
+    this.setMatrixUniforms(shaderKey);
+
+    gl.drawElements(
+      gl.TRIANGLES, this.gHash[gId].len[i],
+       gl.UNSIGNED_SHORT, 0);
+  }.bind(this));
+};
+
+module.exports = p5.Renderer3D;
+},{"../core/core":48}],38:[function(_dereq_,module,exports){
+
+
+module.exports = {
+  vertexColorVert:
+    "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n  vec4 positionVec4 = vec4(aPosition / uResolution * vec3(1.0, -1.0, 1.0), 1.0);\n  gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n  vColor = aVertexColor;\n}",
+  vertexColorFrag:
+    "precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n  gl_FragColor = vColor;\n}",
+  normalVert:
+    "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat4 uNormalMatrix;\nuniform float uResolution;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n  vec4 positionVec4 = vec4(aPosition / uResolution, 1.0);\n  gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n  vVertexNormal = vec3( uNormalMatrix * vec4( aNormal, 1.0 ) );\n  vVertTexCoord = aTexCoord;\n}",
+  normalFrag:
+    "precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n  gl_FragColor = vec4(vVertexNormal, 1.0);\n}",
+  basicFrag:
+    "precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n  gl_FragColor = uMaterialColor;\n}",
+  lightVert:
+    "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat4 uNormalMatrix;\nuniform float uResolution;\nuniform int uAmbientLightCount;\nuniform int uDirectionalLightCount;\nuniform int uPointLightCount;\n\nuniform vec3 uAmbientColor[8];\nuniform vec3 uLightingDirection[8];\nuniform vec3 uDirectionalColor[8];\nuniform vec3 uPointLightLocation[8];\nuniform vec3 uPointLightColor[8];\nuniform bool uSpecular;\n\nvarying vec3 vVertexNormal;\nvarying vec2 vVertTexCoord;\nvarying vec3 vLightWeighting;\n\nvec3 ambientLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 directionalLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor2 = vec3(0.0, 0.0, 0.0);\n\nvoid main(void){\n\n  vec4 positionVec4 = vec4(aPosition / uResolution, 1.0);\n  gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\n  vec3 vertexNormal = vec3( uNormalMatrix * vec4( aNormal, 1.0 ) );\n  vVertexNormal = vertexNormal;\n  vVertTexCoord = aTexCoord;\n\n  vec4 mvPosition = uModelViewMatrix * vec4(aPosition / uResolution, 1.0);\n  vec3 eyeDirection = normalize(-mvPosition.xyz);\n\n  float shininess = 32.0;\n  float specularFactor = 2.0;\n  float diffuseFactor = 0.3;\n\n  for(int i = 0; i < 8; i++){\n    if(uAmbientLightCount == i) break;\n    ambientLightFactor += uAmbientColor[i];\n  }\n\n  for(int j = 0; j < 8; j++){\n    if(uDirectionalLightCount == j) break;\n    vec3 dir = uLightingDirection[j];\n    float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);\n    directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;\n  }\n\n  for(int k = 0; k < 8; k++){\n    if(uPointLightCount == k) break;\n    vec3 loc = uPointLightLocation[k];\n    //loc = loc / uResolution;\n    vec3 lightDirection = normalize(loc - mvPosition.xyz);\n\n    float directionalLightWeighting = max(dot(vertexNormal, lightDirection), 0.0);\n    pointLightFactor += uPointLightColor[k] * directionalLightWeighting;\n\n    //factor2 for specular\n    vec3 reflectionDirection = reflect(-lightDirection, vertexNormal);\n    float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);\n\n    pointLightFactor2 += uPointLightColor[k] * (specularFactor * specularLightWeighting\n      +  directionalLightWeighting * diffuseFactor);\n  }\n  \n  if(!uSpecular){\n    vLightWeighting =  ambientLightFactor + directionalLightFactor + pointLightFactor;\n  }else{\n    vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor2;\n  }\n\n}",
+  lightTextureFrag:
+    "precision mediump float;\n\nuniform vec4 uMaterialColor;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nvarying vec3 vLightWeighting;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n  if(!isTexture){\n    gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n  }else{\n    vec4 textureColor = texture2D(uSampler, vVertTexCoord);\n    if(vLightWeighting == vec3(0., 0., 0.)){\n      gl_FragColor = textureColor;\n    }else{\n      gl_FragColor = vec4(vec3(textureColor.rgb * vLightWeighting), textureColor.a); \n    }\n  }\n}"
+};
+},{}],39:[function(_dereq_,module,exports){
+
+'use strict';
+
+var p5 = _dereq_('./core/core');
+_dereq_('./color/p5.Color');
+_dereq_('./core/p5.Element');
+_dereq_('./typography/p5.Font');
+_dereq_('./core/p5.Graphics');
+_dereq_('./core/p5.Renderer2D');
+
+_dereq_('./image/p5.Image');
+_dereq_('./math/p5.Vector');
+_dereq_('./io/p5.TableRow');
+_dereq_('./io/p5.Table');
+
+_dereq_('./color/creating_reading');
+_dereq_('./color/setting');
+_dereq_('./core/constants');
+_dereq_('./utilities/conversion');
+_dereq_('./utilities/array_functions');
+_dereq_('./utilities/string_functions');
+_dereq_('./core/environment');
+_dereq_('./image/image');
+_dereq_('./image/loading_displaying');
+_dereq_('./image/pixels');
+_dereq_('./io/files');
+_dereq_('./events/keyboard');
+_dereq_('./events/acceleration'); //john
+_dereq_('./events/mouse');
+_dereq_('./utilities/time_date');
+_dereq_('./events/touch');
+_dereq_('./math/math');
+_dereq_('./math/calculation');
+_dereq_('./math/random');
+_dereq_('./math/noise');
+_dereq_('./math/trigonometry');
+_dereq_('./core/rendering');
+_dereq_('./core/2d_primitives');
+
+_dereq_('./core/attributes');
+_dereq_('./core/curves');
+_dereq_('./core/vertex');
+_dereq_('./core/structure');
+_dereq_('./core/transform');
+_dereq_('./typography/attributes');
+_dereq_('./typography/loading_displaying');
+
+_dereq_('./3d/p5.Renderer3D');
+_dereq_('./3d/p5.Geometry3D');
+_dereq_('./3d/retainedMode3D');
+_dereq_('./3d/immediateMode3D');
+_dereq_('./3d/3d_primitives');
+_dereq_('./3d/p5.Matrix');
+_dereq_('./3d/material');
+_dereq_('./3d/light');
+_dereq_('./3d/shader');
+_dereq_('./3d/camera');
+_dereq_('./3d/interaction');
+
+/**
+ * _globalInit
+ *
+ * TODO: ???
+ * if sketch is on window
+ * assume "global" mode
+ * and instantiate p5 automatically
+ * otherwise do nothing
+ *
+ * @return {Undefined}
+ */
+var _globalInit = function() {
+  if (!window.PHANTOMJS && !window.mocha) {
+    // If there is a setup or draw function on the window
+    // then instantiate p5 in "global" mode
+    if((window.setup && typeof window.setup === 'function') ||
+      (window.draw && typeof window.draw === 'function')) {
+      new p5();
+    }
+  }
+};
+
+// TODO: ???
+if (document.readyState === 'complete') {
+  _globalInit();
+} else {
+  window.addEventListener('load', _globalInit , false);
+}
+
+module.exports = p5;
+},{"./3d/3d_primitives":28,"./3d/camera":29,"./3d/immediateMode3D":30,"./3d/interaction":31,"./3d/light":32,"./3d/material":33,"./3d/p5.Geometry3D":34,"./3d/p5.Matrix":35,"./3d/p5.Renderer3D":36,"./3d/retainedMode3D":37,"./3d/shader":38,"./color/creating_reading":41,"./color/p5.Color":42,"./color/setting":43,"./core/2d_primitives":44,"./core/attributes":45,"./core/constants":47,"./core/core":48,"./core/curves":49,"./core/environment":50,"./core/p5.Element":52,"./core/p5.Graphics":53,"./core/p5.Renderer2D":55,"./core/rendering":56,"./core/structure":58,"./core/transform":59,"./core/vertex":60,"./events/acceleration":61,"./events/keyboard":62,"./events/mouse":63,"./events/touch":64,"./image/image":66,"./image/loading_displaying":67,"./image/p5.Image":68,"./image/pixels":69,"./io/files":70,"./io/p5.Table":71,"./io/p5.TableRow":72,"./math/calculation":73,"./math/math":74,"./math/noise":75,"./math/p5.Vector":76,"./math/random":78,"./math/trigonometry":79,"./typography/attributes":80,"./typography/loading_displaying":81,"./typography/p5.Font":82,"./utilities/array_functions":83,"./utilities/conversion":84,"./utilities/string_functions":85,"./utilities/time_date":86}],40:[function(_dereq_,module,exports){
+/**
+ * module Conversion
+ * submodule Color Conversion
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+/**
+ * Conversions adapted from <http://www.easyrgb.com/math.html>.
+ *
+ * In these functions, hue is always in the range [0,1); all other components
+ * are in the range [0,1]. 'Brightness' and 'value' are used interchangeably.
+ */
+
+var p5 = _dereq_('../core/core');
+p5.ColorConversion = {};
+
+/**
+ * Convert an HSBA array to HSLA.
+ */
+p5.ColorConversion._hsbaToHSLA = function(hsba) {
+  var hue = hsba[0];
+  var sat = hsba[1];
+  var val = hsba[2];
+
+  // Calculate lightness.
+  var li = (2 - sat) * val / 2;
+
+  // Convert saturation.
+  if (li !== 0) {
+    if (li === 1) {
+      sat = 0;
+    } else if (li < 0.5) {
+      sat = sat / (2 - sat);
+    } else {
+      sat = sat * val / (2 - li * 2);
+    }
+  }
+
+  // Hue and alpha stay the same.
+  return [hue, sat, li, hsba[3]];
+};
+
+/**
+ * Convert an HSBA array to RGBA.
+ */
+p5.ColorConversion._hsbaToRGBA = function(hsba) {
+  var hue = hsba[0] * 6;  // We will split hue into 6 sectors.
+  var sat = hsba[1];
+  var val = hsba[2];
+
+  var RGBA = [];
+
+  if (sat === 0) {
+    RGBA = [val, val, val, hsba[3]];  // Return early if grayscale.
+  } else {
+    var sector = Math.floor(hue);
+    var tint1 = val * (1 - sat);
+    var tint2 = val * (1 - sat * (hue - sector));
+    var tint3 = val * (1 - sat * (1 + sector - hue));
+    var red, green, blue;
+    if (sector === 0) {  // Red to yellow.
+      red = val;
+      green = tint3;
+      blue = tint1;
+    } else if (sector === 1) {  // Yellow to green.
+      red = tint2;
+      green = val;
+      blue = tint1;
+    } else if (sector === 2) {  // Green to cyan.
+      red = tint1;
+      green = val;
+      blue = tint3;
+    } else if (sector === 3) {  // Cyan to blue.
+      red = tint1;
+      green = tint2;
+      blue = val;
+    } else if (sector === 4) {  // Blue to magenta.
+      red = tint3;
+      green = tint1;
+      blue = val;
+    } else {  // Magenta to red.
+      red = val;
+      green = tint1;
+      blue = tint2;
+    }
+    RGBA = [red, green, blue, hsba[3]];
+  }
+
+  return RGBA;
+};
+
+/**
+ * Convert an HSLA array to HSBA.
+ */
+p5.ColorConversion._hslaToHSBA = function(hsla) {
+  var hue = hsla[0];
+  var sat = hsla[1];
+  var li = hsla[2];
+
+  // Calculate brightness.
+  var val;
+  if (li < 0.5) {
+    val = (1 + sat) * li;
+  } else {
+    val = li + sat - li * sat;
+  }
+
+  // Convert saturation.
+  sat = 2 * (val - li) / val;
+
+  // Hue and alpha stay the same.
+  return [hue, sat, val, hsla[3]];
+};
+
+/**
+ * Convert an HSLA array to RGBA.
+ *
+ * We need to change basis from HSLA to something that can be more easily be
+ * projected onto RGBA. We will choose hue and brightness as our first two
+ * components, and pick a convenient third one ('zest') so that we don't need
+ * to calculate formal HSBA saturation.
+ */
+p5.ColorConversion._hslaToRGBA = function(hsla){
+  var hue = hsla[0] * 6;  // We will split hue into 6 sectors.
+  var sat = hsla[1];
+  var li = hsla[2];
+
+  var RGBA = [];
+
+  if (sat === 0) {
+    RGBA = [li, li, li, hsla[3]]; // Return early if grayscale.
+  } else {
+
+    // Calculate brightness.
+    var val;
+    if (li < 0.5) {
+      val = (1 + sat) * li;
+    } else {
+      val = li + sat - li * sat;
+    }
+
+    // Define zest.
+    var zest = 2 * li - val;
+
+    // Implement projection (project onto green by default).
+    var hzvToRGB = function(hue, zest, val) {
+      if (hue < 0) {  // Hue must wrap to allow projection onto red and blue.
+        hue += 6;
+      } else if (hue >= 6) {
+        hue -= 6;
+      }
+      if (hue < 1) {  // Red to yellow (increasing green).
+        return (zest + (val - zest) * hue);
+      } else if (hue < 3) {  // Yellow to cyan (greatest green).
+        return val;
+      } else if (hue < 4) {  // Cyan to blue (decreasing green).
+        return (zest + (val - zest) * (4 - hue));
+      } else {  // Blue to red (least green).
+        return zest;
+      }
+    };
+
+    // Perform projections, offsetting hue as necessary.
+    RGBA = [hzvToRGB(hue + 2, zest, val),
+            hzvToRGB(hue    , zest, val),
+            hzvToRGB(hue - 2, zest, val),
+            hsla[3]];
+  }
+
+  return RGBA;
+};
+
+/**
+ * Convert an RGBA array to HSBA.
+ */
+p5.ColorConversion._rgbaToHSBA = function(rgba) {
+  var red = rgba[0];
+  var green = rgba[1];
+  var blue = rgba[2];
+
+  var val = Math.max(red, green, blue);
+  var chroma = val - Math.min(red, green, blue);
+
+  var hue, sat;
+  if (chroma === 0) {  // Return early if grayscale.
+    hue = 0;
+    sat = 0;
+  }
+  else {
+    sat = chroma / val;
+    if (red === val) {  // Magenta to yellow.
+      hue = (green - blue) / chroma;
+    } else if (green === val) { // Yellow to cyan.
+      hue = 2 + (blue - red) / chroma;
+    } else if (blue === val) {  // Cyan to magenta.
+      hue = 4 + (red - green) / chroma;
+    }
+    if (hue < 0) {  // Confine hue to the interval [0, 1).
+      hue += 6;
+    } else if (hue >= 6) {
+      hue -= 6;
+    }
+  }
+
+  return [hue / 6, sat, val, rgba[3]];
+};
+
+/**
+ * Convert an RGBA array to HSLA.
+ */
+p5.ColorConversion._rgbaToHSLA = function(rgba) {
+  var red = rgba[0];
+  var green = rgba[1];
+  var blue = rgba[2];
+
+  var val = Math.max(red, green, blue);
+  var min = Math.min(red, green, blue);
+  var li = val + min;  // We will halve this later.
+  var chroma = val - min;
+
+  var hue, sat;
+  if (chroma === 0) {  // Return early if grayscale.
+    hue = 0;
+    sat = 0;
+  } else {
+    if (li < 1) {
+      sat = chroma / li;
+    } else {
+      sat = chroma / (2 - chroma);
+    }
+    if (red === val) {  // Magenta to yellow.
+      hue = (green - blue) / chroma;
+    } else if (green === val) {  // Yellow to cyan.
+      hue = 2 + (blue - red) / chroma;
+    } else if (blue === val) {  // Cyan to magenta.
+      hue = 4 + (red - green) / chroma;
+    }
+    if (hue < 0) {  // Confine hue to the interval [0, 1).
+      hue += 6;
+    } else if (hue >= 6) {
+      hue -= 6;
+    }
+  }
+
+  return [hue / 6, sat, li / 2, rgba[3]];
+};
+
+module.exports = p5.ColorConversion;
+
+},{"../core/core":48}],41:[function(_dereq_,module,exports){
+/**
+ * @module Color
+ * @submodule Creating & Reading
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+_dereq_('./p5.Color');
+
+/**
+ * Extracts the alpha value from a color or pixel array.
+ *
+ * @method alpha
+ * @param {Object} obj p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * c = color(0, 126, 255, 102);
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ * value = alpha(c);  // Sets 'value' to 102
+ * fill(value);
+ * rect(50, 15, 35, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.alpha = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getAlpha();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+/**
+ * Extracts the blue value from a color or pixel array.
+ *
+ * @method blue
+ * @param {Object} obj p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * c = color(175, 100, 220);  // Define color 'c'
+ * fill(c);  // Use color variable 'c' as fill color
+ * rect(15, 20, 35, 60);  // Draw left rectangle
+ *
+ * blueValue = blue(c);  // Get blue in 'c'
+ * println(blueValue);  // Prints "220.0"
+ * fill(0, 0, blueValue);  // Use 'blueValue' in new fill
+ * rect(50, 20, 35, 60);  // Draw right rectangle
+ * </code>
+ * </div>
+ */
+p5.prototype.blue = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getBlue();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+/**
+ * Extracts the HSB brightness value from a color or pixel array.
+ *
+ * @method brightness
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 255);
+ * c = color(0, 126, 255);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = brightness(c);  // Sets 'value' to 255
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.brightness = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getBrightness();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+/**
+ * Creates colors for storing in variables of the color datatype. The
+ * parameters are interpreted as RGB or HSB values depending on the
+ * current colorMode(). The default mode is RGB values from 0 to 255
+ * and, therefore, the function call color(255, 204, 0) will return a
+ * bright yellow color.
+ * <br><br>
+ * Note that if only one value is provided to color(), it will be interpreted
+ * as a grayscale value. Add a second value, and it will be used for alpha
+ * transparency. When three values are specified, they are interpreted as
+ * either RGB or HSB values. Adding a fourth value applies alpha
+ * transparency. If a single string parameter is provided it will be
+ * interpreted as a CSS-compatible color string.
+ *
+ * Colors are stored as Numbers or Arrays.
+ *
+ * @method color
+ * @param  {Number|String} v1      gray value or red or hue value relative to
+ *                                 the current color range, or a color string
+ * @param  {Number}        [v2]    gray value or green or saturation value
+ *                                 relative to the current color range (or
+ *                                 alpha value if first param is gray value)
+ * @param  {Number}        [v3]    gray value or blue or brightness value
+ *                                 relative to the current color range
+ * @param  {Number}        [alpha] alpha value relative to current color range
+ * @return {Array}                 resulting color
+ *
+ * @example
+ * <div>
+ * <code>
+ * var c = color(255, 204, 0);  // Define color 'c'
+ * fill(c);  // Use color variable 'c' as fill color
+ * noStroke();  // Don't draw a stroke around shapes
+ * rect(30, 20, 55, 55);  // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var c = color(255, 204, 0);  // Define color 'c'
+ * fill(c);  // Use color variable 'c' as fill color
+ * noStroke();  // Don't draw a stroke around shapes
+ * ellipse(25, 25, 80, 80);  // Draw left circle
+ *
+ * // Using only one value with color()
+ * // generates a grayscale value.
+ * var c = color(65);  // Update 'c' with grayscale value
+ * fill(c);  // Use updated 'c' as fill color
+ * ellipse(75, 75, 80, 80);  // Draw right circle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG & CSS colors may be used,
+ * var c = color('magenta');
+ * fill(c);  // Use 'c' as fill color
+ * noStroke();  // Don't draw a stroke around shapes
+ * rect(20, 20, 60, 60);  // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // as can hex color codes:
+ * noStroke();  // Don't draw a stroke around shapes
+ * var c = color('#0f0');
+ * fill(c);  // Use 'c' as fill color
+ * rect(0, 10, 45, 80);  // Draw rectangle
+ *
+ * c = color('#00ff00');
+ * fill(c);  // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80);  // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // RGB and RGBA color strings are also supported:
+ * // these all set to the same color (solid blue)
+ * var c;
+ * noStroke();  // Don't draw a stroke around shapes
+ * c = color('rgb(0,0,255)');
+ * fill(c); // Use 'c' as fill color
+ * rect(10, 10, 35, 35);  // Draw rectangle
+ *
+ * c = color('rgb(0%, 0%, 100%)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 35, 35);  // Draw rectangle
+ *
+ * c = color('rgba(0, 0, 255, 1)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(10, 55, 35, 35);  // Draw rectangle
+ *
+ * c = color('rgba(0%, 0%, 100%, 1)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 55, 35, 35);  // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // HSL color is also supported and can be specified
+ * // by value
+ * var c;
+ * noStroke();  // Don't draw a stroke around shapes
+ * c = color('hsl(160, 100%, 50%)');
+ * fill(c);  // Use 'c' as fill color
+ * rect(0, 10, 45, 80);  // Draw rectangle
+ *
+ * c = color('hsla(160, 100%, 50%, 0.5)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80);  // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // HSB color is also supported and can be specified
+ * // by value
+ * var c;
+ * noStroke();  // Don't draw a stroke around shapes
+ * c = color('hsb(160, 100%, 50%)');
+ * fill(c);  // Use 'c' as fill color
+ * rect(0, 10, 45, 80);  // Draw rectangle
+ *
+ * c = color('hsba(160, 100%, 50%, 0.5)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80);  // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var c;  // Declare color 'c'
+ * noStroke();  // Don't draw a stroke around shapes
+ *
+ * // If no colorMode is specified, then the
+ * // default of RGB with scale of 0-255 is used.
+ * c = color(50, 55, 100);  // Create a color for 'c'
+ * fill(c);  // Use color variable 'c' as fill color
+ * rect(0, 10, 45, 80);  // Draw left rect
+ *
+ * colorMode(HSB, 100);  // Use HSB with scale of 0-100
+ * c = color(50, 55, 100);  // Update 'c' with new color
+ * fill(c);  // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80);  // Draw right rect
+ * </code>
+ * </div>
+ */
+p5.prototype.color = function() {
+  if (arguments[0] instanceof p5.Color) {
+    return arguments[0];  // Do nothing if argument is already a color object.
+  } else if (arguments[0] instanceof Array) {
+    if (this instanceof p5.Renderer) {
+      return new p5.Color(this, arguments[0]);
+    } else {
+      return new p5.Color(this._renderer, arguments[0]);
+    }
+  } else {
+    if (this instanceof p5.Renderer) {
+      return new p5.Color(this, arguments);
+    } else {
+      return new p5.Color(this._renderer, arguments);
+    }
+  }
+};
+
+/**
+ * Extracts the green value from a color or pixel array.
+ *
+ * @method green
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * c = color(20, 75, 200);  // Define color 'c'
+ * fill(c);  // Use color variable 'c' as fill color
+ * rect(15, 20, 35, 60);  // Draw left rectangle
+ *
+ * greenValue = green(c);  // Get green in 'c'
+ * println(greenValue);  // Print "75.0"
+ * fill(0, greenValue, 0);  // Use 'greenValue' in new fill
+ * rect(50, 20, 35, 60);  // Draw right rectangle
+ * </code>
+ * </div>
+ */
+p5.prototype.green = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getGreen();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+/**
+ * Extracts the hue value from a color or pixel array.
+ *
+ * Hue exists in both HSB and HSL. This function will return the
+ * HSB-normalized hue when supplied with an HSB color object (or when supplied
+ * with a pixel array while the color mode is HSB), but will default to the
+ * HSL-normalized hue otherwise. (The values will only be different if the
+ * maximum hue setting for each system is different.)
+ *
+ * @method hue
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 255);
+ * c = color(0, 126, 255);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = hue(c);  // Sets 'value' to "0"
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.hue = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getHue();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+/**
+ * Blends two colors to find a third color somewhere between them. The amt
+ * parameter is the amount to interpolate between the two values where 0.0
+ * equal to the first color, 0.1 is very near the first color, 0.5 is halfway
+ * in between, etc. An amount below 0 will be treated as 0. Likewise, amounts
+ * above 1 will be capped at 1. This is different from the behavior of lerp(),
+ * but necessary because otherwise numbers outside the range will produce
+ * strange and unexpected colors.
+ * <br><br>
+ * The way that colours are interpolated depends on the current color mode.
+ *
+ * @method lerpColor
+ * @param  {Array/Number} c1  interpolate from this color
+ * @param  {Array/Number} c2  interpolate to this color
+ * @param  {Number}       amt number between 0 and 1
+ * @return {Array/Number}     interpolated color
+ * @example
+ * <div>
+ * <code>
+ * colorMode(RGB);
+ * stroke(255);
+ * background(51);
+ * from = color(218, 165, 32);
+ * to = color(72, 61, 139);
+ * colorMode(RGB);  // Try changing to HSB.
+ * interA = lerpColor(from, to, .33);
+ * interB = lerpColor(from, to, .66);
+ * fill(from);
+ * rect(10, 20, 20, 60);
+ * fill(interA);
+ * rect(30, 20, 20, 60);
+ * fill(interB);
+ * rect(50, 20, 20, 60);
+ * fill(to);
+ * rect(70, 20, 20, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.lerpColor = function(c1, c2, amt) {
+  var mode = this._renderer._colorMode;
+  var maxes = this._renderer._colorMaxes;
+  var l0, l1, l2, l3;
+  var fromArray, toArray;
+
+  if (mode === constants.RGB) {
+    fromArray = c1.levels.map(function(level) {
+      return level / 255;
+    });
+    toArray = c2.levels.map(function(level) {
+      return level / 255;
+    });
+  } else if (mode === constants.HSB) {
+    c1._getBrightness();  // Cache hsba so it definitely exists.
+    c2._getBrightness();
+    fromArray = c1.hsba;
+    toArray = c2.hsba;
+  } else if (mode === constants.HSL) {
+    c1._getLightness();  // Cache hsla so it definitely exists.
+    c2._getLightness();
+    fromArray = c1.hsla;
+    toArray = c2.hsla;
+  } else {
+    throw new Error (mode + 'cannot be used for interpolation.');
+  }
+
+  // Prevent extrapolation.
+  amt = Math.max(Math.min(amt, 1), 0);
+
+  // Perform interpolation.
+  l0 = this.lerp(fromArray[0], toArray[0], amt);
+  l1 = this.lerp(fromArray[1], toArray[1], amt);
+  l2 = this.lerp(fromArray[2], toArray[2], amt);
+  l3 = this.lerp(fromArray[3], toArray[3], amt);
+
+  // Scale components.
+  l0 *= maxes[mode][0];
+  l1 *= maxes[mode][1];
+  l2 *= maxes[mode][2];
+  l3 *= maxes[mode][3];
+
+  return this.color(l0, l1, l2, l3);
+};
+
+/**
+ * Extracts the HSL lightness value from a color or pixel array.
+ *
+ * @method lightness
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSL);
+ * c = color(156, 100, 50, 1);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = lightness(c);  // Sets 'value' to 50
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.lightness = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getLightness();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+/**
+ * Extracts the red value from a color or pixel array.
+ *
+ * @method red
+ * @param {Object} obj p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * c = color(255, 204, 0);  // Define color 'c'
+ * fill(c);  // Use color variable 'c' as fill color
+ * rect(15, 20, 35, 60);  // Draw left rectangle
+ *
+ * redValue = red(c);  // Get red in 'c'
+ * println(redValue);  // Print "255.0"
+ * fill(redValue, 0, 0);  // Use 'redValue' in new fill
+ * rect(50, 20, 35, 60);  // Draw right rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * colorMode(RGB, 255);
+ * var c = color(127, 255, 0);
+ * colorMode(RGB, 1);
+ * var myColor = red(c);
+ * print(myColor);
+ * </code>
+ * </div>
+ */
+p5.prototype.red = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getRed();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+/**
+ * Extracts the saturation value from a color or pixel array.
+ *
+ * Saturation is scaled differently in HSB and HSL. This function will return
+ * the HSB saturation when supplied with an HSB color object (or when supplied
+ * with a pixel array while the color mode is HSB), but will default to the
+ * HSL saturation otherwise.
+ *
+ * @method saturation
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 255);
+ * c = color(0, 126, 255);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = saturation(c);  // Sets 'value' to 126
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.saturation = function(c) {
+  if (c instanceof p5.Color || c instanceof Array) {
+    return this.color(c)._getSaturation();
+  } else {
+    throw new Error('Needs p5.Color or pixel array as argument.');
+  }
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"./p5.Color":42}],42:[function(_dereq_,module,exports){
+/**
+ * @module Color
+ * @submodule Creating & Reading
+ * @for p5
+ * @requires core
+ * @requires constants
+ * @requires color_conversion
+ */
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+var color_conversion = _dereq_('./color_conversion');
+
+/**
+ * We define colors to be immutable objects. Each color stores the color mode
+ * and level maxes that applied at the time of its construction. These are
+ * used to interpret the input arguments and to format the output e.g. when
+ * saturation() is requested.
+ *
+ * Internally we store an array representing the ideal RGBA values in floating
+ * point form, normalized from 0 to 1. From this we calculate the closest
+ * screen color (RGBA levels from 0 to 255) and expose this to the renderer.
+ *
+ * We also cache normalized, floating point components of the color in various
+ * representations as they are calculated. This is done to prevent repeating a
+ * conversion that has already been performed.
+ *
+ * @class p5.Color
+ * @constructor
+ */
+p5.Color = function(renderer, vals) {
+
+  // Record color mode and maxes at time of construction.
+  this.mode = renderer._colorMode;
+  this.maxes = renderer._colorMaxes;
+
+  // Calculate normalized RGBA values.
+  if (this.mode !== constants.RGB &&
+      this.mode !== constants.HSL &&
+      this.mode !== constants.HSB) {
+    throw new Error(this.mode + ' is an invalid colorMode.');
+  } else {
+    this._array = p5.Color._parseInputs.apply(renderer, vals);
+  }
+
+  // Expose closest screen color.
+  this.levels = this._array.map(function(level) {
+    return Math.round(level * 255);
+  });
+
+  return this;
+};
+
+p5.Color.prototype.toString = function() {
+  var a = this.levels;
+  a[3] = this._array[3];  // String representation uses normalized alpha.
+  return 'rgba('+a[0]+','+a[1]+','+a[2]+','+ a[3] +')';
+};
+
+p5.Color.prototype._getAlpha = function() {
+  return this._array[3] * this.maxes[this.mode][3];
+};
+
+p5.Color.prototype._getBlue = function() {
+  return this._array[2] * this.maxes[constants.RGB][2];
+};
+
+p5.Color.prototype._getBrightness = function() {
+  if (!this.hsba) {
+    this.hsba = color_conversion._rgbaToHSBA(this._array);
+  }
+  return this.hsba[2] * this.maxes[constants.HSB][2];
+};
+
+p5.Color.prototype._getGreen = function() {
+  return this._array[1] * this.maxes[constants.RGB][1];
+};
+
+/**
+ * Hue is the same in HSB and HSL, but the maximum value may be different.
+ * This function will return the HSB-normalized saturation when supplied with
+ * an HSB color object, but will default to the HSL-normalized saturation
+ * otherwise.
+ */
+p5.Color.prototype._getHue = function() {
+  if (this.mode === constants.HSB) {
+    if (!this.hsba) {
+      this.hsba = color_conversion._rgbaToHSBA(this._array);
+    }
+    return this.hsba[0] * this.maxes[constants.HSB][0];
+  } else {
+    if (!this.hsla) {
+      this.hsla = color_conversion._rgbaToHSLA(this._array);
+    }
+    return this.hsla[0] * this.maxes[constants.HSL][0];
+  }
+};
+
+p5.Color.prototype._getLightness = function() {
+  if (!this.hsla) {
+    this.hsla = color_conversion._rgbaToHSLA(this._array);
+  }
+  return this.hsla[2] * this.maxes[constants.HSL][2];
+};
+
+p5.Color.prototype._getRed = function() {
+  return this._array[0] * this.maxes[constants.RGB][0];
+};
+
+/**
+ * Saturation is scaled differently in HSB and HSL. This function will return
+ * the HSB saturation when supplied with an HSB color object, but will default
+ * to the HSL saturation otherwise.
+ */
+p5.Color.prototype._getSaturation = function() {
+  if (this.mode === constants.HSB) {
+    if (!this.hsba) {
+      this.hsba = color_conversion._rgbaToHSBA(this._array);
+    }
+    return this.hsba[1] * this.maxes[constants.HSB][1];
+  } else {
+    if (!this.hsla) {
+      this.hsla = color_conversion._rgbaToHSLA(this._array);
+    }
+    return this.hsla[1] * this.maxes[constants.HSL][1];
+  }
+};
+
+/**
+ * CSS named colors.
+ */
+var namedColors = {
+  aliceblue:             '#f0f8ff',
+  antiquewhite:          '#faebd7',
+  aqua:                  '#00ffff',
+  aquamarine:            '#7fffd4',
+  azure:                 '#f0ffff',
+  beige:                 '#f5f5dc',
+  bisque:                '#ffe4c4',
+  black:                 '#000000',
+  blanchedalmond:        '#ffebcd',
+  blue:                  '#0000ff',
+  blueviolet:            '#8a2be2',
+  brown:                 '#a52a2a',
+  burlywood:             '#deb887',
+  cadetblue:             '#5f9ea0',
+  chartreuse:            '#7fff00',
+  chocolate:             '#d2691e',
+  coral:                 '#ff7f50',
+  cornflowerblue:        '#6495ed',
+  cornsilk:              '#fff8dc',
+  crimson:               '#dc143c',
+  cyan:                  '#00ffff',
+  darkblue:              '#00008b',
+  darkcyan:              '#008b8b',
+  darkgoldenrod:         '#b8860b',
+  darkgray:              '#a9a9a9',
+  darkgreen:             '#006400',
+  darkgrey:              '#a9a9a9',
+  darkkhaki:             '#bdb76b',
+  darkmagenta:           '#8b008b',
+  darkolivegreen:        '#556b2f',
+  darkorange:            '#ff8c00',
+  darkorchid:            '#9932cc',
+  darkred:               '#8b0000',
+  darksalmon:            '#e9967a',
+  darkseagreen:          '#8fbc8f',
+  darkslateblue:         '#483d8b',
+  darkslategray:         '#2f4f4f',
+  darkslategrey:         '#2f4f4f',
+  darkturquoise:         '#00ced1',
+  darkviolet:            '#9400d3',
+  deeppink:              '#ff1493',
+  deepskyblue:           '#00bfff',
+  dimgray:               '#696969',
+  dimgrey:               '#696969',
+  dodgerblue:            '#1e90ff',
+  firebrick:             '#b22222',
+  floralwhite:           '#fffaf0',
+  forestgreen:           '#228b22',
+  fuchsia:               '#ff00ff',
+  gainsboro:             '#dcdcdc',
+  ghostwhite:            '#f8f8ff',
+  gold:                  '#ffd700',
+  goldenrod:             '#daa520',
+  gray:                  '#808080',
+  green:                 '#008000',
+  greenyellow:           '#adff2f',
+  grey:                  '#808080',
+  honeydew:              '#f0fff0',
+  hotpink:               '#ff69b4',
+  indianred:             '#cd5c5c',
+  indigo:                '#4b0082',
+  ivory:                 '#fffff0',
+  khaki:                 '#f0e68c',
+  lavender:              '#e6e6fa',
+  lavenderblush:         '#fff0f5',
+  lawngreen:             '#7cfc00',
+  lemonchiffon:          '#fffacd',
+  lightblue:             '#add8e6',
+  lightcoral:            '#f08080',
+  lightcyan:             '#e0ffff',
+  lightgoldenrodyellow:  '#fafad2',
+  lightgray:             '#d3d3d3',
+  lightgreen:            '#90ee90',
+  lightgrey:             '#d3d3d3',
+  lightpink:             '#ffb6c1',
+  lightsalmon:           '#ffa07a',
+  lightseagreen:         '#20b2aa',
+  lightskyblue:          '#87cefa',
+  lightslategray:        '#778899',
+  lightslategrey:        '#778899',
+  lightsteelblue:        '#b0c4de',
+  lightyellow:           '#ffffe0',
+  lime:                  '#00ff00',
+  limegreen:             '#32cd32',
+  linen:                 '#faf0e6',
+  magenta:               '#ff00ff',
+  maroon:                '#800000',
+  mediumaquamarine:      '#66cdaa',
+  mediumblue:            '#0000cd',
+  mediumorchid:          '#ba55d3',
+  mediumpurple:          '#9370db',
+  mediumseagreen:        '#3cb371',
+  mediumslateblue:       '#7b68ee',
+  mediumspringgreen:     '#00fa9a',
+  mediumturquoise:       '#48d1cc',
+  mediumvioletred:       '#c71585',
+  midnightblue:          '#191970',
+  mintcream:             '#f5fffa',
+  mistyrose:             '#ffe4e1',
+  moccasin:              '#ffe4b5',
+  navajowhite:           '#ffdead',
+  navy:                  '#000080',
+  oldlace:               '#fdf5e6',
+  olive:                 '#808000',
+  olivedrab:             '#6b8e23',
+  orange:                '#ffa500',
+  orangered:             '#ff4500',
+  orchid:                '#da70d6',
+  palegoldenrod:         '#eee8aa',
+  palegreen:             '#98fb98',
+  paleturquoise:         '#afeeee',
+  palevioletred:         '#db7093',
+  papayawhip:            '#ffefd5',
+  peachpuff:             '#ffdab9',
+  peru:                  '#cd853f',
+  pink:                  '#ffc0cb',
+  plum:                  '#dda0dd',
+  powderblue:            '#b0e0e6',
+  purple:                '#800080',
+  red:                   '#ff0000',
+  rosybrown:             '#bc8f8f',
+  royalblue:             '#4169e1',
+  saddlebrown:           '#8b4513',
+  salmon:                '#fa8072',
+  sandybrown:            '#f4a460',
+  seagreen:              '#2e8b57',
+  seashell:              '#fff5ee',
+  sienna:                '#a0522d',
+  silver:                '#c0c0c0',
+  skyblue:               '#87ceeb',
+  slateblue:             '#6a5acd',
+  slategray:             '#708090',
+  slategrey:             '#708090',
+  snow:                  '#fffafa',
+  springgreen:           '#00ff7f',
+  steelblue:             '#4682b4',
+  tan:                   '#d2b48c',
+  teal:                  '#008080',
+  thistle:               '#d8bfd8',
+  tomato:                '#ff6347',
+  turquoise:             '#40e0d0',
+  violet:                '#ee82ee',
+  wheat:                 '#f5deb3',
+  white:                 '#ffffff',
+  whitesmoke:            '#f5f5f5',
+  yellow:                '#ffff00',
+  yellowgreen:           '#9acd32'
+};
+
+/**
+ * These regular expressions are used to build up the patterns for matching
+ * viable CSS color strings: fragmenting the regexes in this way increases the
+ * legibility and comprehensibility of the code.
+ *
+ * Note that RGB values of .9 are not parsed by IE, but are supported here for
+ * color string consistency.
+ */
+var WHITESPACE = /\s*/;  // Match zero or more whitespace characters.
+var INTEGER = /(\d{1,3})/;  // Match integers: 79, 255, etc.
+var DECIMAL = /((?:\d+(?:\.\d+)?)|(?:\.\d+))/;  // Match 129.6, 79, .9, etc.
+var PERCENT = new RegExp(DECIMAL.source + '%');  // Match 12.9%, 79%, .9%, etc.
+
+/**
+ * Full color string patterns. The capture groups are necessary.
+ */
+var colorPatterns = {
+  // Match colors in format #XXX, e.g. #416.
+  HEX3: /^#([a-f0-9])([a-f0-9])([a-f0-9])$/i,
+
+  // Match colors in format #XXXXXX, e.g. #b4d455.
+  HEX6: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i,
+
+  // Match colors in format rgb(R, G, B), e.g. rgb(255, 0, 128).
+  RGB: new RegExp([
+    '^rgb\\(',
+    INTEGER.source,
+    ',',
+    INTEGER.source,
+    ',',
+    INTEGER.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i'),
+
+  // Match colors in format rgb(R%, G%, B%), e.g. rgb(100%, 0%, 28.9%).
+  RGB_PERCENT: new RegExp([
+    '^rgb\\(',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i'),
+
+  // Match colors in format rgb(R, G, B, A), e.g. rgb(255, 0, 128, 0.25).
+  RGBA: new RegExp([
+    '^rgba\\(',
+    INTEGER.source,
+    ',',
+    INTEGER.source,
+    ',',
+    INTEGER.source,
+    ',',
+    DECIMAL.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i'),
+
+  // Match colors in format rgb(R%, G%, B%, A), e.g. rgb(100%, 0%, 28.9%, 0.5).
+  RGBA_PERCENT: new RegExp([
+    '^rgba\\(',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    ',',
+    DECIMAL.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i'),
+
+  // Match colors in format hsla(H, S%, L%), e.g. hsl(100, 40%, 28.9%).
+  HSL: new RegExp([
+    '^hsl\\(',
+    INTEGER.source,
+    ',',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i'),
+
+  // Match colors in format hsla(H, S%, L%, A), e.g. hsla(100, 40%, 28.9%, 0.5).
+  HSLA: new RegExp([
+    '^hsla\\(',
+    INTEGER.source,
+    ',',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    ',',
+    DECIMAL.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i'),
+
+  // Match colors in format hsb(H, S%, B%), e.g. hsb(100, 40%, 28.9%).
+  HSB: new RegExp([
+    '^hsb\\(',
+    INTEGER.source,
+    ',',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i'),
+
+  // Match colors in format hsba(H, S%, B%, A), e.g. hsba(100, 40%, 28.9%, 0.5).
+  HSBA: new RegExp([
+    '^hsba\\(',
+    INTEGER.source,
+    ',',
+    PERCENT.source,
+    ',',
+    PERCENT.source,
+    ',',
+    DECIMAL.source,
+    '\\)$'
+  ].join(WHITESPACE.source), 'i')
+};
+
+/**
+ * For a number of different inputs, returns a color formatted as [r, g, b, a]
+ * arrays, with each component normalized between 0 and 1.
+ *
+ * @param {Array-like} args An 'array-like' object that represents a list of
+ *                          arguments
+ * @return {Array}          a color formatted as [r, g, b, a]
+ *                          Example:
+ *                          input        ==> output
+ *                          g            ==> [g, g, g, 255]
+ *                          g,a          ==> [g, g, g, a]
+ *                          r, g, b      ==> [r, g, b, 255]
+ *                          r, g, b, a   ==> [r, g, b, a]
+ *                          [g]          ==> [g, g, g, 255]
+ *                          [g, a]       ==> [g, g, g, a]
+ *                          [r, g, b]    ==> [r, g, b, 255]
+ *                          [r, g, b, a] ==> [r, g, b, a]
+ * @example
+ * <div>
+ * <code>
+ * // todo
+ * </code>
+ * </div>
+ */
+p5.Color._parseInputs = function() {
+  var numArgs = arguments.length;
+  var mode = this._colorMode;
+  var maxes = this._colorMaxes;
+  var results = [];
+
+  if (numArgs >= 3) {  // Argument is a list of component values.
+
+    results[0] = arguments[0] / maxes[mode][0];
+    results[1] = arguments[1] / maxes[mode][1];
+    results[2] = arguments[2] / maxes[mode][2];
+
+    // Alpha may be undefined, so default it to 100%.
+    if (typeof arguments[3] === 'number') {
+      results[3] = arguments[3] / maxes[mode][3];
+    } else {
+      results[3] = 1;
+    }
+
+    // Constrain components to the range [0,1].
+    results = results.map(function(value) {
+      return Math.max(Math.min(value, 1), 0);
+    });
+
+    // Convert to RGBA and return.
+    if (mode === constants.HSL) {
+      return color_conversion._hslaToRGBA(results);
+    } else if (mode === constants.HSB) {
+      return color_conversion._hsbaToRGBA(results);
+    } else {
+      return results;
+    }
+
+  } else if (numArgs === 1 && typeof arguments[0] === 'string') {
+
+    var str = arguments[0].trim().toLowerCase();
+
+    // Return if string is a named colour.
+    if (namedColors[str]) {
+      return p5.Color._parseInputs.apply(this, [namedColors[str]]);
+    }
+
+    // Try RGBA pattern matching.
+    if (colorPatterns.HEX3.test(str)) {  // #rgb
+      results = colorPatterns.HEX3.exec(str).slice(1).map(function(color) {
+        return parseInt(color + color, 16) / 255;
+      });
+      results[3] = 1;
+      return results;
+    } else if (colorPatterns.HEX6.test(str)) {  // #rrggbb
+      results = colorPatterns.HEX6.exec(str).slice(1).map(function(color) {
+        return parseInt(color, 16) / 255;
+      });
+      results[3] = 1;
+      return results;
+    } else if (colorPatterns.RGB.test(str)) {  // rgb(R,G,B)
+      results = colorPatterns.RGB.exec(str).slice(1).map(function(color) {
+        return color / 255;
+      });
+      results[3] = 1;
+      return results;
+    } else if (colorPatterns.RGB_PERCENT.test(str)) {  // rgb(R%,G%,B%)
+      results = colorPatterns.RGB_PERCENT.exec(str).slice(1)
+        .map(function(color) {
+          return parseFloat(color) / 100;
+        });
+      results[3] = 1;
+      return results;
+    } else if (colorPatterns.RGBA.test(str)) {  // rgba(R,G,B,A)
+      results = colorPatterns.RGBA.exec(str).slice(1)
+        .map(function(color, idx) {
+          if (idx === 3) {
+            return parseFloat(color);
+          }
+          return color / 255;
+        });
+      return results;
+    } else if (colorPatterns.RGBA_PERCENT.test(str)) {  // rgba(R%,G%,B%,A%)
+      results = colorPatterns.RGBA_PERCENT.exec(str).slice(1)
+        .map(function(color, idx) {
+          if (idx === 3) {
+            return parseFloat(color);
+          }
+          return parseFloat(color) / 100;
+        });
+      return results;
+    }
+
+    // Try HSLA pattern matching.
+    if (colorPatterns.HSL.test(str)) {  // hsl(H,S,L)
+      results = colorPatterns.HSL.exec(str).slice(1)
+        .map(function(color, idx) {
+        if (idx === 0) {
+          return parseInt(color, 10) / 360;
+        }
+        return parseInt(color, 10) / 100;
+      });
+      results[3] = 1;
+    } else if (colorPatterns.HSLA.test(str)) {  // hsla(H,S,L,A)
+      results = colorPatterns.HSLA.exec(str).slice(1)
+        .map(function(color, idx) {
+        if (idx === 0) {
+          return parseInt(color, 10) / 360;
+        }
+        else if (idx === 3) {
+          return parseFloat(color);
+        }
+        return parseInt(color, 10) / 100;
+      });
+    }
+    if (results.length) {
+      return color_conversion._hslaToRGBA(results);
+    }
+
+    // Try HSBA pattern matching.
+    if (colorPatterns.HSB.test(str)) {  // hsb(H,S,B)
+      results = colorPatterns.HSB.exec(str).slice(1)
+        .map(function(color, idx) {
+        if (idx === 0) {
+          return parseInt(color, 10) / 360;
+        }
+        return parseInt(color, 10) / 100;
+      });
+      results[3] = 1;
+    } else if (colorPatterns.HSBA.test(str)) {  // hsba(H,S,B,A)
+      results = colorPatterns.HSBA.exec(str).slice(1)
+        .map(function(color, idx) {
+        if (idx === 0) {
+          return parseInt(color, 10) / 360;
+        }
+        else if (idx === 3) {
+          return parseFloat(color);
+        }
+        return parseInt(color, 10) / 100;
+      });
+    }
+    if (results.length) {
+      return color_conversion._hsbaToRGBA(results);
+    }
+
+    // Input did not match any CSS color pattern: default to white.
+    results = [1, 1, 1, 1];
+
+  } else if ((numArgs === 1 || numArgs === 2) &&
+              typeof arguments[0] === 'number') {  // 'Grayscale' mode.
+
+    /**
+     * For HSB and HSL, interpret the gray level as a brightness/lightness
+     * value (they are equivalent when chroma is zero). For RGB, normalize the
+     * gray level according to the blue maximum.
+     */
+    results[0] = arguments[0] / maxes[mode][2];
+    results[1] = arguments[0] / maxes[mode][2];
+    results[2] = arguments[0] / maxes[mode][2];
+
+    // Alpha may be undefined, so default it to 100%.
+    if (typeof arguments[1] === 'number') {
+      results[3] = arguments[1] / maxes[mode][3];
+    } else {
+      results[3] = 1;
+    }
+
+    // Constrain components to the range [0,1].
+    results = results.map(function(value) {
+      return Math.max(Math.min(value, 1), 0);
+    });
+
+  } else {
+    throw new Error (arguments + 'is not a valid color representation.');
+  }
+
+  return results;
+};
+
+module.exports = p5.Color;
+
+},{"../core/constants":47,"../core/core":48,"./color_conversion":40}],43:[function(_dereq_,module,exports){
+/**
+ * @module Color
+ * @submodule Setting
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+_dereq_('./p5.Color');
+
+/**
+ * The background() function sets the color used for the background of the
+ * p5.js canvas. The default background is light gray. This function is
+ * typically used within draw() to clear the display window at the beginning
+ * of each frame, but it can be used inside setup() to set the background on
+ * the first frame of animation or if the background need only be set once.
+ *
+ * @method background
+ * @param {Number|String|p5.Color|p5.Image} v1   gray value, red or hue value
+ *                                               (depending on the current
+ *                                               color mode), color string,
+ *                                               p5.Color, or p5.Image
+ * @param {Number}                          [v2] green or saturation value
+ *                                               (depending on the current
+ *                                               color mode)
+ * @param {Number}                          [v3] blue or brightness value
+ *                                               (depending on the current
+ *                                               color mode)
+ * @param {Number}                          [a]  opacity of the background
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Grayscale integer value
+ * background(51);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // R, G & B integer values
+ * background(255, 204, 0);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // H, S & B integer values
+ * colorMode(HSB);
+ * background(255, 204, 100);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG/CSS color string
+ * background('red');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // three-digit hexadecimal RGB notation
+ * background('#fae');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // six-digit hexadecimal RGB notation
+ * background('#222222');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGB notation
+ * background('rgb(0,255,0)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGBA notation
+ * background('rgba(0,255,0, 0.25)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGB notation
+ * background('rgb(100%,0%,10%)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGBA notation
+ * background('rgba(100%,0%,100%,0.5)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // p5 Color object
+ * background(color(0, 0, 255));
+ * </code>
+ * </div>
+ */
+p5.prototype.background = function() {
+  if (arguments[0] instanceof p5.Image) {
+    this.image(arguments[0], 0, 0, this.width, this.height);
+  } else {
+    this._renderer.background.apply(this._renderer, arguments);
+  }
+  return this;
+};
+
+/**
+ * Clears the pixels within a buffer. This function only works on p5.Canvas
+ * objects created with the createCanvas() function; it won't work with the
+ * main display window. Unlike the main graphics context, pixels in
+ * additional graphics areas created with createGraphics() can be entirely
+ * or partially transparent. This function clears everything to make all of
+ * the pixels 100% transparent.
+ *
+ * @method clear
+ * @example
+ * <div>
+ * <code>
+ * // Clear the screen on mouse press.
+ * function setup() {
+ *   createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ *   ellipse(mouseX, mouseY, 20, 20);
+ * }
+ *
+ * function mousePressed() {
+ *   clear();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.clear = function() {
+  this._renderer.clear();
+  return this;
+};
+
+/**
+ * colorMode() changes the way p5.js interprets color data. By default, the
+ * parameters for fill(), stroke(), background(), and color() are defined by
+ * values between 0 and 255 using the RGB color model. This is equivalent to
+ * setting colorMode(RGB, 255). Setting colorMode(HSB) lets you use the HSB
+ * system instead. By default, this is colorMode(HSB, 360, 100, 100, 1). You
+ * can also use HSL.
+ * <br><br>
+ * Note: existing color objects remember the mode that they were created in,
+ * so you can change modes as you like without affecting their appearance.
+ *
+ * @method colorMode
+ * @param {Number|Constant} mode   either RGB or HSB, corresponding to
+ *                                 Red/Green/Blue and Hue/Saturation/Brightness
+ *                                 (or Lightness)
+ * @param {Number|Constant} [max1] range for the red or hue depending on the
+ *                                 current color mode, or range for all values
+ * @param {Number|Constant} [max2] range for the green or saturation depending
+ *                                 on the current color mode
+ * @param {Number|Constant} [max3] range for the blue or brightness/lighntess
+ *                                 depending on the current color mode
+ * @param {Number|Constant} [maxA] range for the alpha
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(RGB, 100);
+ * for (i = 0; i < 100; i++) {
+ *   for (j = 0; j < 100; j++) {
+ *     stroke(i, j, 0);
+ *     point(i, j);
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 100);
+ * for (i = 0; i < 100; i++) {
+ *   for (j = 0; j < 100; j++) {
+ *     stroke(i, j, 100);
+ *     point(i, j);
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * colorMode(RGB, 255);
+ * var c = color(127, 255, 0);
+ *
+ * colorMode(RGB, 1);
+ * var myColor = c._getRed();
+ * text(myColor, 10, 10, 80, 80);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * colorMode(RGB, 255, 255, 255, 1);
+ * background(255);
+ *
+ * strokeWeight(4);
+ * stroke(255, 0 , 10, 0.3);
+ * ellipse(40, 40, 50, 50);
+ * ellipse(50, 50, 40, 40);
+ * </code>
+ * </div>
+ */
+p5.prototype.colorMode = function() {
+  if (arguments[0] === constants.RGB ||
+      arguments[0] === constants.HSB ||
+      arguments[0] === constants.HSL) {
+
+    // Set color mode.
+    this._renderer._colorMode = arguments[0];
+
+    // Set color maxes.
+    var maxes = this._renderer._colorMaxes[this._renderer._colorMode];
+    if (arguments.length === 2) {
+      maxes[0] = arguments[1];  // Red
+      maxes[1] = arguments[1];  // Green
+      maxes[2] = arguments[1];  // Blue
+      maxes[3] = arguments[1];  // Alpha
+    } else if (arguments.length === 4) {
+      maxes[0] = arguments[1];  // Red
+      maxes[1] = arguments[2];  // Green
+      maxes[2] = arguments[3];  // Blue
+    } else if (arguments.length === 5) {
+      maxes[0] = arguments[1];  // Red
+      maxes[1] = arguments[2];  // Green
+      maxes[2] = arguments[3];  // Blue
+      maxes[3] = arguments[4];  // Alpha
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Sets the color used to fill shapes. For example, if you run
+ * fill(204, 102, 0), all subsequent shapes will be filled with orange. This
+ * color is either specified in terms of the RGB or HSB color depending on
+ * the current colorMode(). (The default color space is RGB, with each value
+ * in the range from 0 to 255).
+ * <br><br>
+ * If a single string argument is provided, RGB, RGBA and Hex CSS color strings
+ * and all named color strings are supported. A p5 Color object can also be
+ * provided to set the fill color.
+ *
+ * @method fill
+ * @param {Number|Array|String|p5.Color} v1   gray value, red or hue value
+ *                                            (depending on the current color
+ *                                            mode), or color Array, or CSS
+ *                                            color string
+ * @param {Number}                       [v2] green or saturation value
+ *                                            (depending on the current
+ *                                            color mode)
+ * @param {Number}                       [v3] blue or brightness value
+ *                                            (depending on the current
+ *                                            color mode)
+ * @param {Number}                       [a]  opacity of the background
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Grayscale integer value
+ * fill(51);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // R, G & B integer values
+ * fill(255, 204, 0);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // H, S & B integer values
+ * colorMode(HSB);
+ * fill(255, 204, 100);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG/CSS color string
+ * fill('red');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // three-digit hexadecimal RGB notation
+ * fill('#fae');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // six-digit hexadecimal RGB notation
+ * fill('#222222');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGB notation
+ * fill('rgb(0,255,0)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGBA notation
+ * fill('rgba(0,255,0, 0.25)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGB notation
+ * fill('rgb(100%,0%,10%)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGBA notation
+ * fill('rgba(100%,0%,100%,0.5)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // p5 Color object
+ * fill(color(0, 0, 255));
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.fill = function() {
+  this._renderer._setProperty('_fillSet', true);
+  this._renderer._setProperty('_doFill', true);
+  this._renderer.fill.apply(this._renderer, arguments);
+  return this;
+};
+
+/**
+ * Disables filling geometry. If both noStroke() and noFill() are called,
+ * nothing will be drawn to the screen.
+ *
+ * @method noFill
+ * @example
+ * <div>
+ * <code>
+ * rect(15, 10, 55, 55);
+ * noFill();
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.noFill = function() {
+  this._renderer._setProperty('_doFill', false);
+  return this;
+};
+
+/**
+ * Disables drawing the stroke (outline). If both noStroke() and noFill()
+ * are called, nothing will be drawn to the screen.
+ *
+ * @method noStroke
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.noStroke = function() {
+  this._renderer._setProperty('_doStroke', false);
+  return this;
+};
+
+/**
+ * Sets the color used to draw lines and borders around shapes. This color
+ * is either specified in terms of the RGB or HSB color depending on the
+ * current colorMode() (the default color space is RGB, with each value in
+ * the range from 0 to 255).
+ * <br><br>
+ * If a single string argument is provided, RGB, RGBA and Hex CSS color
+ * strings and all named color strings are supported. A p5 Color object
+ * can also be provided to set the stroke color.
+ *
+ * @method stroke
+ * @param {Number|Array|String|p5.Color} v1   gray value, red or hue value
+ *                                            (depending on the current color
+ *                                            mode), or color Array, or CSS
+ *                                            color string
+ * @param {Number}                       [v2] green or saturation value
+ *                                            (depending on the current
+ *                                            color mode)
+ * @param {Number}                       [v3] blue or brightness value
+ *                                            (depending on the current
+ *                                            color mode)
+ * @param {Number}                       [a]  opacity of the background
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Grayscale integer value
+ * strokeWeight(4);
+ * stroke(51);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // R, G & B integer values
+ * stroke(255, 204, 0);
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // H, S & B integer values
+ * colorMode(HSB);
+ * strokeWeight(4);
+ * stroke(255, 204, 100);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG/CSS color string
+ * stroke('red');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // three-digit hexadecimal RGB notation
+ * stroke('#fae');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // six-digit hexadecimal RGB notation
+ * stroke('#222222');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGB notation
+ * stroke('rgb(0,255,0)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGBA notation
+ * stroke('rgba(0,255,0,0.25)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGB notation
+ * stroke('rgb(100%,0%,10%)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGBA notation
+ * stroke('rgba(100%,0%,100%,0.5)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // p5 Color object
+ * stroke(color(0, 0, 255));
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.stroke = function() {
+  this._renderer._setProperty('_strokeSet', true);
+  this._renderer._setProperty('_doStroke', true);
+  this._renderer.stroke.apply(this._renderer, arguments);
+  return this;
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"./p5.Color":42}],44:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule 2D Primitives
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+_dereq_('./error_helpers');
+
+/**
+ * Draw an arc to the screen. If called with only a, b, c, d, start, and
+ * stop, the arc will be drawn as an open pie. If mode is provided, the arc
+ * will be drawn either open, as a chord, or as a pie as specified. The
+ * origin may be changed with the ellipseMode() function.<br><br>
+ * Note that drawing a full circle (ex: 0 to TWO_PI) will appear blank
+ * because 0 and TWO_PI are the same position on the unit circle. The
+ * best way to handle this is by using the ellipse() function instead
+ * to create a closed ellipse, and to use the arc() function
+ * only to draw parts of an ellipse.
+ *
+ * @method arc
+ * @param  {Number} a      x-coordinate of the arc's ellipse
+ * @param  {Number} b      y-coordinate of the arc's ellipse
+ * @param  {Number} c      width of the arc's ellipse by default
+ * @param  {Number} d      height of the arc's ellipse by default
+ * @param  {Number} start  angle to start the arc, specified in radians
+ * @param  {Number} stop   angle to stop the arc, specified in radians
+ * @param  {String} [mode] optional parameter to determine the way of drawing
+ *                         the arc
+ * @return {Object}        the p5 object
+ * @example
+ * <div>
+ * <code>
+ * arc(50, 55, 50, 50, 0, HALF_PI);
+ * noFill();
+ * arc(50, 55, 60, 60, HALF_PI, PI);
+ * arc(50, 55, 70, 70, PI, PI+QUARTER_PI);
+ * arc(50, 55, 80, 80, PI+QUARTER_PI, TWO_PI);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, OPEN);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, CHORD);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, PIE);
+ * </code>
+ * </div>
+ */
+p5.prototype.arc = function(x, y, w, h, start, stop, mode) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'arc',
+    args,
+    [
+      ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+      [ 'Number', 'Number', 'Number', 'Number',
+        'Number', 'Number', 'String' ]
+    ]
+  );
+
+  if (!this._renderer._doStroke && !this._renderer._doFill) {
+    return this;
+  }
+  if (this._angleMode === constants.DEGREES) {
+    start = this.radians(start);
+    stop = this.radians(stop);
+  }
+
+  // Make all angles positive...
+  while (start < 0) {
+    start += constants.TWO_PI;
+  }
+  while (stop < 0) {
+    stop += constants.TWO_PI;
+  }
+  // ...and confine them to the interval [0,TWO_PI).
+  start %= constants.TWO_PI;
+  stop %= constants.TWO_PI;
+
+  // Adjust angles to counter linear scaling.
+  if (start <= constants.HALF_PI) {
+    start = Math.atan(w / h * Math.tan(start));
+  } else  if (start > constants.HALF_PI && start <= 3 * constants.HALF_PI) {
+    start = Math.atan(w / h * Math.tan(start)) + constants.PI;
+  } else {
+    start = Math.atan(w / h * Math.tan(start)) + constants.TWO_PI;
+  }
+  if (stop <= constants.HALF_PI) {
+    stop = Math.atan(w / h * Math.tan(stop));
+  } else  if (stop > constants.HALF_PI && stop <= 3 * constants.HALF_PI) {
+    stop = Math.atan(w / h * Math.tan(stop)) + constants.PI;
+  } else {
+    stop = Math.atan(w / h * Math.tan(stop)) + constants.TWO_PI;
+  }
+
+  // Exceed the interval if necessary in order to preserve the size and
+  // orientation of the arc.
+  if (start > stop) {
+    stop += constants.TWO_PI;
+  }
+  // p5 supports negative width and heights for ellipses
+  w = Math.abs(w);
+  h = Math.abs(h);
+  this._renderer.arc(x, y, w, h, start, stop, mode);
+  return this;
+};
+
+/**
+ * Draws an ellipse (oval) to the screen. An ellipse with equal width and
+ * height is a circle. By default, the first two parameters set the location,
+ * and the third and fourth parameters set the shape's width and height. The
+ * origin may be changed with the ellipseMode() function.
+ *
+ * @method ellipse
+ * @param  {Number} a x-coordinate of the ellipse.
+ * @param  {Number} b y-coordinate of the ellipse.
+ * @param  {Number} c width of the ellipse.
+ * @param  {Number} d height of the ellipse.
+ * @return {p5}       the p5 object
+ * @example
+ * <div>
+ * <code>
+ * ellipse(56, 46, 55, 55);
+ * </code>
+ * </div>
+ */
+p5.prototype.ellipse = function(x, y, w, h) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'ellipse',
+    args,
+    ['Number', 'Number', 'Number', 'Number']
+  );
+
+  if (!this._renderer._doStroke && !this._renderer._doFill) {
+    return this;
+  }
+  // p5 supports negative width and heights for ellipses
+  w = Math.abs(w);
+  h = Math.abs(h);
+  //@TODO add catch block here if this._renderer
+  //doesn't have the method implemented yet
+  this._renderer.ellipse(x, y, w, h);
+  return this;
+};
+/**
+ * Draws a line (a direct path between two points) to the screen. The version
+ * of line() with four parameters draws the line in 2D. To color a line, use
+ * the stroke() function. A line cannot be filled, therefore the fill()
+ * function will not affect the color of a line. 2D lines are drawn with a
+ * width of one pixel by default, but this can be changed with the
+ * strokeWeight() function.
+ *
+ * @method line
+ * @param  {Number} x1 the x-coordinate of the first point
+ * @param  {Number} y1 the y-coordinate of the first point
+ * @param  {Number} x2 the x-coordinate of the second point
+ * @param  {Number} y2 the y-coordinate of the second point
+ * @return {p5}        the p5 object
+ * @example
+ * <div>
+ * <code>
+ * line(30, 20, 85, 75);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * line(30, 20, 85, 20);
+ * stroke(126);
+ * line(85, 20, 85, 75);
+ * stroke(255);
+ * line(85, 75, 30, 75);
+ * </code>
+ * </div>
+ */
+////commented out original
+// p5.prototype.line = function(x1, y1, x2, y2) {
+//   if (!this._renderer._doStroke) {
+//     return this;
+//   }
+//   if(this._renderer.isP3D){
+//   } else {
+//     this._renderer.line(x1, y1, x2, y2);
+//   }
+// };
+p5.prototype.line = function() {
+  if (!this._renderer._doStroke) {
+    return this;
+  }
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  //check whether we should draw a 3d line or 2d
+  if(this._renderer.isP3D){
+    this._validateParameters(
+      'line',
+      args,
+      [
+        ['Number', 'Number', 'Number', 'Number', 'Number', 'Number']
+      ]
+    );
+    this._renderer.line(
+      args[0],
+      args[1],
+      args[2],
+      args[3],
+      args[4],
+      args[5]);
+  } else {
+    this._validateParameters(
+      'line',
+      args,
+      [
+        ['Number', 'Number', 'Number', 'Number'],
+      ]
+    );
+    this._renderer.line(
+      args[0],
+      args[1],
+      args[2],
+      args[3]);
+  }
+  return this;
+};
+
+/**
+ * Draws a point, a coordinate in space at the dimension of one pixel.
+ * The first parameter is the horizontal value for the point, the second
+ * value is the vertical value for the point. The color of the point is
+ * determined by the current stroke.
+ *
+ * @method point
+ * @param  {Number} x the x-coordinate
+ * @param  {Number} y the y-coordinate
+ * @return {p5}       the p5 object
+ * @example
+ * <div>
+ * <code>
+ * point(30, 20);
+ * point(85, 20);
+ * point(85, 75);
+ * point(30, 75);
+ * </code>
+ * </div>
+ */
+p5.prototype.point = function() {
+  if (!this._renderer._doStroke) {
+    return this;
+  }
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  //check whether we should draw a 3d line or 2d
+  if(this._renderer.isP3D){
+    this._validateParameters(
+      'point',
+      args,
+      [
+        ['Number', 'Number', 'Number']
+      ]
+    );
+    this._renderer.point(
+      args[0],
+      args[1],
+      args[2]
+      );
+  } else {
+    this._validateParameters(
+      'point',
+      args,
+      [
+        ['Number', 'Number']
+      ]
+    );
+    this._renderer.point(
+      args[0],
+      args[1]
+    );
+  }
+  return this;
+};
+
+
+/**
+ * Draw a quad. A quad is a quadrilateral, a four sided polygon. It is
+ * similar to a rectangle, but the angles between its edges are not
+ * constrained to ninety degrees. The first pair of parameters (x1,y1)
+ * sets the first vertex and the subsequent pairs should proceed
+ * clockwise or counter-clockwise around the defined shape.
+ *
+ * @method quad
+ * @param {Number} x1 the x-coordinate of the first point
+ * @param {Number} y1 the y-coordinate of the first point
+ * @param {Number} x2 the x-coordinate of the second point
+ * @param {Number} y2 the y-coordinate of the second point
+ * @param {Number} x3 the x-coordinate of the third point
+ * @param {Number} y3 the y-coordinate of the third point
+ * @param {Number} x4 the x-coordinate of the fourth point
+ * @param {Number} y4 the y-coordinate of the fourth point
+ * @return {p5}     the p5 object
+ * @example
+ * <div>
+ * <code>
+ * quad(38, 31, 86, 20, 69, 63, 30, 76);
+ * </code>
+ * </div>
+ */
+p5.prototype.quad = function() {
+  if (!this._renderer._doStroke && !this._renderer._doFill) {
+    return this;
+  }
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  if(this._renderer.isP3D){
+    this._validateParameters(
+      'quad',
+      args,
+      [
+        [ 'Number', 'Number', 'Number',
+          'Number', 'Number', 'Number',
+          'Number', 'Number', 'Number',
+          'Number', 'Number', 'Number']
+      ]
+    );
+    this._renderer.quad(
+      args[0],
+      args[1],
+      args[2],
+      args[3],
+      args[4],
+      args[5],
+      args[6],
+      args[7],
+      args[8],
+      args[9],
+      args[10],
+      args[11]
+      );
+  } else {
+    this._validateParameters(
+      'quad',
+      args,
+      [
+        [ 'Number', 'Number', 'Number', 'Number',
+          'Number', 'Number', 'Number', 'Number' ]
+      ]
+    );
+    this._renderer.quad(
+     args[0],
+     args[1],
+     args[2],
+     args[3],
+     args[4],
+     args[5],
+     args[6],
+    args[7]
+    );
+  }
+  return this;
+};
+
+/**
+* Draws a rectangle to the screen. A rectangle is a four-sided shape with
+* every angle at ninety degrees. By default, the first two parameters set
+* the location of the upper-left corner, the third sets the width, and the
+* fourth sets the height. The way these parameters are interpreted, however,
+* may be changed with the rectMode() function.
+* <br><br>
+* The fifth, sixth, seventh and eighth parameters, if specified,
+* determine corner radius for the top-right, top-left, lower-right and
+* lower-left corners, respectively. An omitted corner radius parameter is set
+* to the value of the previously specified radius value in the parameter list.
+*
+* @method rect
+* @param  {Number} x  x-coordinate of the rectangle.
+* @param  {Number} y  y-coordinate of the rectangle.
+* @param  {Number} w  width of the rectangle.
+* @param  {Number} h  height of the rectangle.
+* @param  {Number} [tl] optional radius of top-left corner.
+* @param  {Number} [tr] optional radius of top-right corner.
+* @param  {Number} [br] optional radius of bottom-right corner.
+* @param  {Number} [bl] optional radius of bottom-left corner.
+* @return {p5}          the p5 object.
+* @example
+* <div>
+* <code>
+* // Draw a rectangle at location (30, 20) with a width and height of 55.
+* rect(30, 20, 55, 55);
+* </code>
+* </div>
+*
+* <div>
+* <code>
+* // Draw a rectangle with rounded corners, each having a radius of 20.
+* rect(30, 20, 55, 55, 20);
+* </code>
+* </div>
+*
+* <div>
+* <code>
+* // Draw a rectangle with rounded corners having the following radii:
+* // top-left = 20, top-right = 15, bottom-right = 10, bottom-left = 5.
+* rect(30, 20, 55, 55, 20, 15, 10, 5)
+* </code>
+* </div>
+*/
+p5.prototype.rect = function (x, y, w, h, tl, tr, br, bl) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'rect',
+    args,
+    [
+      ['Number', 'Number', 'Number', 'Number'],
+      ['Number', 'Number', 'Number', 'Number', 'Number'],
+      ['Number', 'Number', 'Number', 'Number',
+       'Number', 'Number', 'Number', 'Number']
+    ]
+  );
+
+  if (!this._renderer._doStroke && !this._renderer._doFill) {
+    return;
+  }
+  this._renderer.rect(x, y, w, h, tl, tr, br, bl);
+  return this;
+};
+
+/**
+* A triangle is a plane created by connecting three points. The first two
+* arguments specify the first point, the middle two arguments specify the
+* second point, and the last two arguments specify the third point.
+*
+* @method triangle
+* @param  {Number} x1 x-coordinate of the first point
+* @param  {Number} y1 y-coordinate of the first point
+* @param  {Number} x2 x-coordinate of the second point
+* @param  {Number} y2 y-coordinate of the second point
+* @param  {Number} x3 x-coordinate of the third point
+* @param  {Number} y3 y-coordinate of the third point
+* @return {p5}        the p5 object
+* @example
+* <div>
+* <code>
+* triangle(30, 75, 58, 20, 86, 75);
+* </code>
+* </div>
+*/
+p5.prototype.triangle = function() {
+
+  if (!this._renderer._doStroke && !this._renderer._doFill) {
+    return this;
+  }
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  if(this._renderer.isP3D){
+    this._validateParameters(
+      'triangle',
+      args,
+      [
+        ['Number', 'Number', 'Number', 'Number', 'Number', 'Number',
+         'Number', 'Number', 'Number']
+      ]
+    );
+    this._renderer.triangle(
+      args[0],
+      args[1],
+      args[2],
+      args[3],
+      args[4],
+      args[5],
+      args[6],
+      args[7],
+      args[8]
+      );
+  } else {
+    this._validateParameters(
+      'triangle',
+      args,
+      [
+        ['Number', 'Number', 'Number', 'Number', 'Number', 'Number']
+      ]
+    );
+    this._renderer.triangle(
+     args[0],
+     args[1],
+     args[2],
+     args[3],
+     args[4],
+     args[5]
+    );
+  }
+  return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48,"./error_helpers":51}],45:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule Attributes
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+/**
+ * Modifies the location from which ellipses are drawn by changing the way
+ * in which parameters given to ellipse() are interpreted.
+ * <br><br>
+ * The default mode is ellipseMode(CENTER), which interprets the first two
+ * parameters of ellipse() as the shape's center point, while the third and
+ * fourth parameters are its width and height.
+ * <br><br>
+ * ellipseMode(RADIUS) also uses the first two parameters of ellipse() as
+ * the shape's center point, but uses the third and fourth parameters to
+ * specify half of the shapes's width and height.
+ * <br><br>
+ * ellipseMode(CORNER) interprets the first two parameters of ellipse() as
+ * the upper-left corner of the shape, while the third and fourth parameters
+ * are its width and height.
+ * <br><br>
+ * ellipseMode(CORNERS) interprets the first two parameters of ellipse() as
+ * the location of one corner of the ellipse's bounding box, and the third
+ * and fourth parameters as the location of the opposite corner.
+ * <br><br>
+ * The parameter must be written in ALL CAPS because Javascript is a
+ * case-sensitive language.
+ *
+ * @method ellipseMode
+ * @param  {Number/Constant} mode either CENTER, RADIUS, CORNER, or CORNERS
+ * @return {p5}                   the p5 object
+ * @example
+ * <div>
+ * <code>
+ * ellipseMode(RADIUS);  // Set ellipseMode to RADIUS
+ * fill(255);  // Set fill to white
+ * ellipse(50, 50, 30, 30);  // Draw white ellipse using RADIUS mode
+ *
+ * ellipseMode(CENTER);  // Set ellipseMode to CENTER
+ * fill(100);  // Set fill to gray
+ * ellipse(50, 50, 30, 30);  // Draw gray ellipse using CENTER mode
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * ellipseMode(CORNER);  // Set ellipseMode is CORNER
+ * fill(255);  // Set fill to white
+ * ellipse(25, 25, 50, 50);  // Draw white ellipse using CORNER mode
+ *
+ * ellipseMode(CORNERS);  // Set ellipseMode to CORNERS
+ * fill(100);  // Set fill to gray
+ * ellipse(25, 25, 50, 50);  // Draw gray ellipse using CORNERS mode
+ * </code>
+ * </div>
+ */
+p5.prototype.ellipseMode = function(m) {
+  if (m === constants.CORNER ||
+    m === constants.CORNERS ||
+    m === constants.RADIUS ||
+    m === constants.CENTER) {
+    this._renderer._ellipseMode = m;
+  }
+  return this;
+};
+
+/**
+ * Draws all geometry with jagged (aliased) edges. Note that smooth() is
+ * active by default, so it is necessary to call noSmooth() to disable
+ * smoothing of geometry, images, and fonts.
+ *
+ * @method noSmooth
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(0);
+ * noStroke();
+ * smooth();
+ * ellipse(30, 48, 36, 36);
+ * noSmooth();
+ * ellipse(70, 48, 36, 36);
+ * </code>
+ * </div>
+ */
+p5.prototype.noSmooth = function() {
+  this._renderer.noSmooth();
+  return this;
+};
+
+/**
+ * Modifies the location from which rectangles are drawn by changing the way
+ * in which parameters given to rect() are interpreted.
+ * <br><br>
+ * The default mode is rectMode(CORNER), which interprets the first two
+ * parameters of rect() as the upper-left corner of the shape, while the
+ * third and fourth parameters are its width and height.
+ * <br><br>
+ * rectMode(CORNERS) interprets the first two parameters of rect() as the
+ * location of one corner, and the third and fourth parameters as the
+ * location of the opposite corner.
+ * <br><br>
+ * rectMode(CENTER) interprets the first two parameters of rect() as the
+ * shape's center point, while the third and fourth parameters are its
+ * width and height.
+ * <br><br>
+ * rectMode(RADIUS) also uses the first two parameters of rect() as the
+ * shape's center point, but uses the third and fourth parameters to specify
+ * half of the shapes's width and height.
+ * <br><br>
+ * The parameter must be written in ALL CAPS because Javascript is a
+ * case-sensitive language.
+ *
+ * @method rectMode
+ * @param  {Number/Constant} mode either CORNER, CORNERS, CENTER, or RADIUS
+ * @return {p5}                   the p5 object
+ * @example
+ * <div>
+ * <code>
+ * rectMode(CORNER);  // Default rectMode is CORNER
+ * fill(255);  // Set fill to white
+ * rect(25, 25, 50, 50);  // Draw white rect using CORNER mode
+ *
+ * rectMode(CORNERS);  // Set rectMode to CORNERS
+ * fill(100);  // Set fill to gray
+ * rect(25, 25, 50, 50);  // Draw gray rect using CORNERS mode
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * rectMode(RADIUS);  // Set rectMode to RADIUS
+ * fill(255);  // Set fill to white
+ * rect(50, 50, 30, 30);  // Draw white rect using RADIUS mode
+ *
+ * rectMode(CENTER);  // Set rectMode to CENTER
+ * fill(100);  // Set fill to gray
+ * rect(50, 50, 30, 30);  // Draw gray rect using CENTER mode
+ * </code>
+ * </div>
+ */
+p5.prototype.rectMode = function(m) {
+  if (m === constants.CORNER ||
+    m === constants.CORNERS ||
+    m === constants.RADIUS ||
+    m === constants.CENTER) {
+    this._renderer._rectMode = m;
+  }
+  return this;
+};
+
+/**
+ * Draws all geometry with smooth (anti-aliased) edges. smooth() will also
+ * improve image quality of resized images. Note that smooth() is active by
+ * default; noSmooth() can be used to disable smoothing of geometry,
+ * images, and fonts.
+ *
+ * @method smooth
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(0);
+ * noStroke();
+ * smooth();
+ * ellipse(30, 48, 36, 36);
+ * noSmooth();
+ * ellipse(70, 48, 36, 36);
+ * </code>
+ * </div>
+ */
+p5.prototype.smooth = function() {
+  this._renderer.smooth();
+  return this;
+};
+
+/**
+ * Sets the style for rendering line endings. These ends are either squared,
+ * extended, or rounded, each of which specified with the corresponding
+ * parameters: SQUARE, PROJECT, and ROUND. The default cap is ROUND.
+ *
+ * @method strokeCap
+ * @param  {Number/Constant} cap either SQUARE, PROJECT, or ROUND
+ * @return {p5}                  the p5 object
+ * @example
+ * <div>
+ * <code>
+ * strokeWeight(12.0);
+ * strokeCap(ROUND);
+ * line(20, 30, 80, 30);
+ * strokeCap(SQUARE);
+ * line(20, 50, 80, 50);
+ * strokeCap(PROJECT);
+ * line(20, 70, 80, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.strokeCap = function(cap) {
+  if (cap === constants.ROUND ||
+    cap === constants.SQUARE ||
+    cap === constants.PROJECT) {
+    this._renderer.strokeCap(cap);
+  }
+  return this;
+};
+
+/**
+ * Sets the style of the joints which connect line segments. These joints
+ * are either mitered, beveled, or rounded and specified with the
+ * corresponding parameters MITER, BEVEL, and ROUND. The default joint is
+ * MITER.
+ *
+ * @method strokeJoin
+ * @param  {Number/Constant} join either MITER, BEVEL, ROUND
+ * @return {p5}                   the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(10.0);
+ * strokeJoin(MITER);
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(10.0);
+ * strokeJoin(BEVEL);
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(10.0);
+ * strokeJoin(ROUND);
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.strokeJoin = function(join) {
+  if (join === constants.ROUND ||
+    join === constants.BEVEL ||
+    join === constants.MITER) {
+    this._renderer.strokeJoin(join);
+  }
+  return this;
+};
+
+/**
+ * Sets the width of the stroke used for lines, points, and the border
+ * around shapes. All widths are set in units of pixels.
+ *
+ * @method strokeWeight
+ * @param  {Number} weight the weight (in pixels) of the stroke
+ * @return {p5}            the p5 object
+ * @example
+ * <div>
+ * <code>
+ * strokeWeight(1);  // Default
+ * line(20, 20, 80, 20);
+ * strokeWeight(4);  // Thicker
+ * line(20, 40, 80, 40);
+ * strokeWeight(10);  // Beastly
+ * line(20, 70, 80, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.strokeWeight = function(w) {
+  this._renderer.strokeWeight(w);
+  return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],46:[function(_dereq_,module,exports){
+/**
+ * @requires constants
+ */
+
+var constants = _dereq_('./constants');
+
+module.exports = {
+
+  modeAdjust: function(a, b, c, d, mode) {
+    if (mode === constants.CORNER) {
+      return { x: a, y: b, w: c, h: d };
+    } else if (mode === constants.CORNERS) {
+      return { x: a, y: b, w: c-a, h: d-b };
+    } else if (mode === constants.RADIUS) {
+      return { x: a-c, y: b-d, w: 2*c, h: 2*d };
+    } else if (mode === constants.CENTER) {
+      return { x: a-c*0.5, y: b-d*0.5, w: c, h: d };
+    }
+  },
+
+  arcModeAdjust: function(a, b, c, d, mode) {
+    if (mode === constants.CORNER) {
+      return { x: a+c*0.5, y: b+d*0.5, w: c, h: d };
+    } else if (mode === constants.CORNERS) {
+      return { x: a, y: b, w: c+a, h: d+b };
+    } else if (mode === constants.RADIUS) {
+      return { x: a, y: b, w: 2*c, h: 2*d };
+    } else if (mode === constants.CENTER) {
+      return { x: a, y: b, w: c, h: d };
+    }
+  }
+
+};
+
+
+},{"./constants":47}],47:[function(_dereq_,module,exports){
+/**
+ * @module Constants
+ * @submodule Constants
+ * @for p5
+ */
+
+var PI = Math.PI;
+
+module.exports = {
+
+  // GRAPHICS RENDERER
+  P2D: 'p2d',
+  WEBGL: 'webgl',
+
+  // ENVIRONMENT
+  ARROW: 'default',
+  CROSS: 'crosshair',
+  HAND: 'pointer',
+  MOVE: 'move',
+  TEXT: 'text',
+  WAIT: 'wait',
+
+  // TRIGONOMETRY
+
+  /**
+   * HALF_PI is a mathematical constant with the value
+   * 1.57079632679489661923. It is half the ratio of the
+   * circumference of a circle to its diameter. It is useful in
+   * combination with the trigonometric functions sin() and cos().
+   *
+   * @property HALF_PI
+   *
+   * @example
+   * <div><code>
+   * arc(50, 50, 80, 80, 0, HALF_PI);
+   * </code></div>
+   *
+   */
+  HALF_PI: PI / 2,
+  /**
+   * PI is a mathematical constant with the value
+   * 3.14159265358979323846. It is the ratio of the circumference
+   * of a circle to its diameter. It is useful in combination with
+   * the trigonometric functions sin() and cos().
+   *
+   * @property PI
+   *
+   * @example
+   * <div><code>
+   * arc(50, 50, 80, 80, 0, PI);
+   * </code></div>
+   */
+  PI: PI,
+  /**
+   * QUARTER_PI is a mathematical constant with the value 0.7853982.
+   * It is one quarter the ratio of the circumference of a circle to
+   * its diameter. It is useful in combination with the trigonometric
+   * functions sin() and cos().
+   *
+   * @property QUARTER_PI
+   *
+   * @example
+   * <div><code>
+   * arc(50, 50, 80, 80, 0, QUARTER_PI);
+   * </code></div>
+   *
+   */
+  QUARTER_PI: PI / 4,
+  /**
+   * TAU is an alias for TWO_PI, a mathematical constant with the
+   * value 6.28318530717958647693. It is twice the ratio of the
+   * circumference of a circle to its diameter. It is useful in
+   * combination with the trigonometric functions sin() and cos().
+   *
+   * @property TAU
+   *
+   * @example
+   * <div><code>
+   * arc(50, 50, 80, 80, 0, TAU);
+   * </code></div>
+   *
+   */
+  TAU: PI * 2,
+  /**
+   * TWO_PI is a mathematical constant with the value
+   * 6.28318530717958647693. It is twice the ratio of the
+   * circumference of a circle to its diameter. It is useful in
+   * combination with the trigonometric functions sin() and cos().
+   *
+   * @property TWO_PI
+   *
+   * @example
+   * <div><code>
+   * arc(50, 50, 80, 80, 0, TWO_PI);
+   * </code></div>
+   *
+   */
+  TWO_PI: PI * 2,
+  DEGREES: 'degrees',
+  RADIANS: 'radians',
+
+  // SHAPE
+  CORNER: 'corner',
+  CORNERS: 'corners',
+  RADIUS: 'radius',
+  RIGHT: 'right',
+  LEFT: 'left',
+  CENTER: 'center',
+  TOP: 'top',
+  BOTTOM: 'bottom',
+  BASELINE: 'alphabetic',
+  POINTS: 'points',
+  LINES: 'lines',
+  TRIANGLES: 'triangles',
+  TRIANGLE_FAN: 'triangles_fan',
+  TRIANGLE_STRIP: 'triangles_strip',
+  QUADS: 'quads',
+  QUAD_STRIP: 'quad_strip',
+  CLOSE: 'close',
+  OPEN: 'open',
+  CHORD: 'chord',
+  PIE: 'pie',
+  PROJECT: 'square', // PEND: careful this is counterintuitive
+  SQUARE: 'butt',
+  ROUND: 'round',
+  BEVEL: 'bevel',
+  MITER: 'miter',
+
+  // COLOR
+  RGB: 'rgb',
+  HSB: 'hsb',
+  HSL: 'hsl',
+
+  // DOM EXTENSION
+  AUTO: 'auto',
+
+  // INPUT
+  ALT: 18,
+  BACKSPACE: 8,
+  CONTROL: 17,
+  DELETE: 46,
+  DOWN_ARROW: 40,
+  ENTER: 13,
+  ESCAPE: 27,
+  LEFT_ARROW: 37,
+  OPTION: 18,
+  RETURN: 13,
+  RIGHT_ARROW: 39,
+  SHIFT: 16,
+  TAB: 9,
+  UP_ARROW: 38,
+
+  // RENDERING
+  BLEND: 'normal',
+  ADD: 'lighter',
+  //ADD: 'add', //
+  //SUBTRACT: 'subtract', //
+  DARKEST: 'darken',
+  LIGHTEST: 'lighten',
+  DIFFERENCE: 'difference',
+  EXCLUSION: 'exclusion',
+  MULTIPLY: 'multiply',
+  SCREEN: 'screen',
+  REPLACE: 'source-over',
+  OVERLAY: 'overlay',
+  HARD_LIGHT: 'hard-light',
+  SOFT_LIGHT: 'soft-light',
+  DODGE: 'color-dodge',
+  BURN: 'color-burn',
+
+  // FILTERS
+  THRESHOLD: 'threshold',
+  GRAY: 'gray',
+  OPAQUE: 'opaque',
+  INVERT: 'invert',
+  POSTERIZE: 'posterize',
+  DILATE: 'dilate',
+  ERODE: 'erode',
+  BLUR: 'blur',
+
+  // TYPOGRAPHY
+  NORMAL: 'normal',
+  ITALIC: 'italic',
+  BOLD: 'bold',
+
+  // TYPOGRAPHY-INTERNAL
+  _DEFAULT_TEXT_FILL: '#000000',
+  _DEFAULT_LEADMULT: 1.25,
+  _CTX_MIDDLE: 'middle',
+
+  // VERTICES
+  LINEAR: 'linear',
+  QUADRATIC: 'quadratic',
+  BEZIER: 'bezier',
+  CURVE: 'curve',
+
+  // DEFAULTS
+  _DEFAULT_STROKE: '#000000',
+  _DEFAULT_FILL: '#FFFFFF'
+
+};
+
+},{}],48:[function(_dereq_,module,exports){
+/**
+ * @module Structure
+ * @submodule Structure
+ * @for p5
+ * @requires constants
+ */
+
+'use strict';
+
+_dereq_('./shim');
+
+// Core needs the PVariables object
+var constants = _dereq_('./constants');
+
+/**
+ * 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 preload(), setup(), and/or
+ * draw() 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
+ *
+ * @param  {Function}    sketch a closure that can set optional preload(),
+ *                              setup(), and/or draw() properties on the
+ *                              given p5 instance
+ * @param  {HTMLElement|boolean} node element to attach canvas to, if a
+ *                                    boolean is passed in use it as sync
+ * @param  {boolean}     [sync] start synchronously (optional)
+ * @return {p5}                 a p5 instance
+ */
+var p5 = function(sketch, node, sync) {
+
+  if (arguments.length === 2 && typeof node === 'boolean') {
+    sync = node;
+    node = undefined;
+  }
+
+  //////////////////////////////////////////////
+  // PUBLIC p5 PROPERTIES AND METHODS
+  //////////////////////////////////////////////
+
+
+  /**
+   * Called directly before setup(), the preload() function is used to handle
+   * asynchronous loading of external files. If a preload function is
+   * defined, setup() will wait until any load calls within have finished.
+   * Nothing besides load calls should be inside preload (loadImage,
+   * loadJSON, loadFont, loadStrings, etc).
+   *
+   * @method preload
+   * @example
+   * <div><code>
+   * var img;
+   * var 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>
+   */
+
+  /**
+   * The setup() 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 setup() function for each program and it shouldn't
+   * be called again after its initial execution.
+   * <br><br>
+   * Note: Variables declared within setup() are not accessible within other
+   * functions, including draw().
+   *
+   * @method setup
+   * @example
+   * <div><code>
+   * var a = 0;
+   *
+   * function setup() {
+   *   background(0);
+   *   noStroke();
+   *   fill(102);
+   * }
+   *
+   * function draw() {
+   *   rect(a++%width, 10, 2, 80);
+   * }
+   * </code></div>
+   */
+
+  /**
+   * Called directly after setup(), the draw() function continuously executes
+   * the lines of code contained inside its block until the program is stopped
+   * or noLoop() is called. draw() is called automatically and should never be
+   * called explicitly.
+   * <br><br>
+   * It should always be controlled with noLoop(), redraw() and loop(). After
+   * noLoop() stops the code in draw() from executing, redraw() causes the
+   * code inside draw() to execute once, and loop() will cause the code
+   * inside draw() to resume executing continuously.
+   * <br><br>
+   * The number of times draw() executes in each second may be controlled with
+   * the frameRate() function.
+   * <br><br>
+   * There can only be one draw() function for each sketch, and draw() must
+   * exist if you want the code to run continuously, or to process events such
+   * as mousePressed(). Sometimes, you might have an empty call to draw() in
+   * your program, as shown in the above example.
+   *
+   * @method draw
+   * @example
+   * <div><code>
+   * var 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>
+   */
+
+
+  //////////////////////////////////////////////
+  // 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._requestAnimId = 0;
+  this._preloadCount = 0;
+  this._isGlobal = false;
+  this._loop = true;
+  this._styles = [];
+  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,
+    'mouseover': null,
+    'mouseout': null,
+    'keydown': null,
+    'keyup': null,
+    'keypress': null,
+    'touchstart': null,
+    'touchmove': null,
+    'touchend': null,
+    'resize': null,
+    'blur': null
+  };
+
+  if (window.DeviceOrientationEvent) {
+    this._events.deviceorientation = null;
+  }
+  if (window.DeviceMotionEvent && !window._isNodeWebkit) {
+    this._events.devicemotion = null;
+  }
+
+  this._events.wheel = null;
+
+
+  this._loadingScreenId = 'p5_loading';
+
+  this._start = function () {
+    // Find node if id given
+    if (this._userNode) {
+      if (typeof this._userNode === 'string') {
+        this._userNode = document.getElementById(this._userNode);
+      }
+    }
+
+    // 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',
+      true
+    );
+
+    var userPreload = this.preload || window.preload; // look for "preload"
+    if (userPreload) {
+
+      // Setup loading screen
+      // Set loading scfeen 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 this._preloadMethods){
+        // default to p5 if no object defined
+        this._preloadMethods[method] = this._preloadMethods[method] || p5;
+        var obj = this._preloadMethods[method];
+        //it's p5, check if it's global or instance
+        if (obj === p5.prototype || obj === p5){
+          obj = this._isGlobal ? window : this;
+        }
+        this._registeredPreloadMethods[method] = obj[method];
+        obj[method] = this._wrapPreload(obj, method);
+      }
+
+      userPreload();
+      this._runIfPreloadsAreDone();
+    } else {
+      this._setup();
+      this._runFrames();
+      this._draw();
+    }
+  }.bind(this);
+
+  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);
+      }
+      context._setup();
+      context._runFrames();
+      context._draw();
+    }
+  };
+
+  this._decrementPreload = function(){
+    var context = this._isGlobal ? window : this;
+    context._setProperty('_preloadCount', context._preloadCount - 1);
+    context._runIfPreloadsAreDone();
+  };
+
+  this._wrapPreload = function(obj, fnName){
+    return function(){
+      //increment counter
+      this._incrementPreload();
+      //call original function
+      var args = Array.prototype.slice.call(arguments);
+      args.push(this._decrementPreload.bind(this));
+      return this._registeredPreloadMethods[fnName].apply(obj, args);
+    }.bind(this);
+  };
+
+  this._incrementPreload = function(){
+    var context = this._isGlobal ? window : this;
+    context._setProperty('_preloadCount', context._preloadCount + 1);
+  };
+
+  this._setup = function() {
+
+    // 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);
+        }
+      }
+    }
+
+    // Short-circuit on this, in case someone used the library in "global"
+    // mode earlier
+    if (typeof context.setup === 'function') {
+      context.setup();
+    }
+
+    // // unhide hidden canvas that was created
+    // this.canvas.style.visibility = '';
+    // this.canvas.className = this.canvas.className.replace('p5_hidden', '');
+
+    // unhide any hidden canvases that were created
+    var reg = new RegExp(/(^|\s)p5_hidden(?!\S)/g);
+    var canvases = document.getElementsByClassName('p5_hidden');
+    for (var i = 0; i < canvases.length; i++) {
+      var k = canvases[i];
+      k.style.visibility = '';
+      k.className = k.className.replace(reg, '');
+    }
+    this._setupDone = true;
+
+  }.bind(this);
+
+  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(matrixs and stack) for 3d
+      if(this._renderer.isP3D){
+        this._renderer._update();
+      }
+
+      this._setProperty('frameCount', this.frameCount + 1);
+      this._updateMouseCoords();
+      this._updateTouchCoords();
+      this.redraw();
+      this._frameRate = 1000.0/(now - this._lastFrameTime);
+      this._lastFrameTime = now;
+    }
+
+    // get notified the next time the browser gives us
+    // an opportunity to draw.
+    if (this._loop) {
+      this._requestAnimId = window.requestAnimationFrame(this._draw);
+    }
+  }.bind(this);
+
+  this._runFrames = function() {
+    if (this._updateInterval) {
+      clearInterval(this._updateInterval);
+    }
+  }.bind(this);
+
+  this._setProperty = function(prop, value) {
+    this[prop] = value;
+    if (this._isGlobal) {
+      window[prop] = value;
+    }
+  }.bind(this);
+
+  /**
+   * 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.
+   * @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>
+   */
+  this.remove = function() {
+    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
+      for (var i=0; i<this._elements.length; i++) {
+        var e = this._elements[i];
+        if (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
+      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;
+            }
+          }
+        }
+      }
+    }
+    // window.p5 = undefined;
+  }.bind(this);
+
+
+  // attach constants to p5 instance
+  for (var k in constants) {
+    p5.prototype[k] = constants[k];
+  }
+
+  // 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;
+    // 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)) {
+          window[p] = p5.prototype[p].bind(this);
+        }
+      } else {
+        window[p] = p5.prototype[p];
+      }
+    }
+    // Attach its properties to the window
+    for (var p2 in this) {
+      if (this.hasOwnProperty(p2)) {
+        window[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);
+  }
+
+  // Bind events to window (not using container div bc key events don't work)
+
+  for (var e in this._events) {
+    var f = this['_on'+e];
+    if (f) {
+      var m = f.bind(this);
+      window.addEventListener(e, m);
+      this._events[e] = m;
+    }
+  }
+
+  var focusHandler = function() {
+    this._setProperty('focused', true);
+  }.bind(this);
+  var blurHandler = function() {
+    this._setProperty('focused', false);
+  }.bind(this);
+  window.addEventListener('focus', focusHandler);
+  window.addEventListener('blur', blurHandler);
+  this.registerMethod('remove', function() {
+    window.removeEventListener('focus', focusHandler);
+    window.removeEventListener('blur', blurHandler);
+  });
+
+  // TODO: ???
+
+  if (sync) {
+    this._start();
+  } else {
+    if (document.readyState === 'complete') {
+      this._start();
+    } else {
+      window.addEventListener('load', this._start.bind(this), false);
+    }
+  }
+};
+
+
+// functions that cause preload to wait
+// more can be added by using registerPreloadMethod(func)
+p5.prototype._preloadMethods = {
+  loadJSON: p5.prototype,
+  loadImage: p5.prototype,
+  loadStrings: p5.prototype,
+  loadXML: p5.prototype,
+  loadShape: p5.prototype,
+  loadTable: p5.prototype,
+  loadFont: p5.prototype
+};
+
+p5.prototype._registeredMethods = { pre: [], post: [], remove: [] };
+
+p5.prototype._registeredPreloadMethods = {};
+
+p5.prototype.registerPreloadMethod = function(fnString, obj) {
+  // obj = obj || p5.prototype;
+  if (!p5.prototype._preloadMethods.hasOwnProperty(fnString)) {
+    p5.prototype._preloadMethods[fnString] = obj;
+  }
+};
+
+p5.prototype.registerMethod = function(name, m) {
+  if (!p5.prototype._registeredMethods.hasOwnProperty(name)) {
+    p5.prototype._registeredMethods[name] = [];
+  }
+  p5.prototype._registeredMethods[name].push(m);
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./shim":57}],49:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule Curves
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+
+_dereq_('./error_helpers');
+
+var bezierDetail = 20;
+var curveDetail = 20;
+
+/**
+ * Draws a cubic Bezier curve on the screen. These curves are defined by a
+ * series of anchor and control points. The first two parameters specify
+ * the first anchor point and the last two parameters specify the other
+ * anchor point, which become the first and last points on the curve. The
+ * middle parameters specify the two control points which define the shape
+ * of the curve. Approximately speaking, control points "pull" the curve
+ * towards them.<br /><br />Bezier curves were developed by French
+ * automotive engineer Pierre Bezier, and are commonly used in computer
+ * graphics to define gently sloping curves. See also curve().
+ *
+ * @method bezier
+ * @param  {Number} x1 x-coordinate for the first anchor point
+ * @param  {Number} y1 y-coordinate for the first anchor point
+ * @param  {Number} x2 x-coordinate for the first control point
+ * @param  {Number} y2 y-coordinate for the first control point
+ * @param  {Number} x3 x-coordinate for the second control point
+ * @param  {Number} y3 y-coordinate for the second control point
+ * @param  {Number} x4 x-coordinate for the second anchor point
+ * @param  {Number} y4 y-coordinate for the second anchor point
+ * @return {Object}    the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * stroke(255, 102, 0);
+ * line(85, 20, 10, 10);
+ * line(90, 90, 15, 80);
+ * stroke(0, 0, 0);
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * </code>
+ * </div>
+ */
+p5.prototype.bezier = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'bezier',
+    args,
+    [ 'Number', 'Number', 'Number', 'Number',
+      'Number', 'Number', 'Number', 'Number' ]
+  );
+
+  if (!this._renderer._doStroke) {
+    return this;
+  }
+  this._renderer.bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+  return this;
+};
+
+/**
+ * Sets the resolution at which Beziers display.
+ *
+ * The default value is 20.
+ *
+ * @param {Number} detail resolution of the curves
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(204);
+ * bezierDetail(50);
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierDetail = function(d) {
+  bezierDetail = d;
+  return this;
+};
+
+/**
+ * Evaluates the Bezier at position t for points a, b, c, d.
+ * The parameters a and d are the first and last points
+ * on the curve, and b and c are the control points.
+ * The final parameter t varies between 0 and 1.
+ * This can be done once with the x coordinates and a second time
+ * with the y coordinates to get the location of a bezier curve at t.
+ *
+ * @method bezierPoint
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} the value of the Bezier at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * x1 = 85, x2 = 10, x3 = 90, x4 = 15;
+ * y1 = 20, y2 = 10, y3 = 90, y4 = 80;
+ * bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+ * fill(255);
+ * steps = 10;
+ * for (i = 0; i <= steps; i++) {
+ *   t = i / steps;
+ *   x = bezierPoint(x1, x2, x3, x4, t);
+ *   y = bezierPoint(y1, y2, y3, y4, t);
+ *   ellipse(x, y, 5, 5);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierPoint = function(a, b, c, d, t) {
+  var adjustedT = 1-t;
+  return Math.pow(adjustedT,3)*a +
+   3*(Math.pow(adjustedT,2))*t*b +
+   3*adjustedT*Math.pow(t,2)*c +
+   Math.pow(t,3)*d;
+};
+
+/**
+ * Evaluates the tangent to the Bezier at position t for points a, b, c, d.
+ * The parameters a and d are the first and last points
+ * on the curve, and b and c are the control points.
+ * The final parameter t varies between 0 and 1.
+ *
+ * @method bezierTangent
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} the tangent at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * steps = 6;
+ * fill(255);
+ * for (i = 0; i <= steps; i++) {
+ *   t = i / steps;
+ *   // Get the location of the point
+ *   x = bezierPoint(85, 10, 90, 15, t);
+ *   y = bezierPoint(20, 10, 90, 80, t);
+ *   // Get the tangent points
+ *   tx = bezierTangent(85, 10, 90, 15, t);
+ *   ty = bezierTangent(20, 10, 90, 80, t);
+ *   // Calculate an angle from the tangent points
+ *   a = atan2(ty, tx);
+ *   a += PI;
+ *   stroke(255, 102, 0);
+ *   line(x, y, cos(a)*30 + x, sin(a)*30 + y);
+ *   // The following line of code makes a line
+ *   // inverse of the above line
+ *   //line(x, y, cos(a)*-30 + x, sin(a)*-30 + y);
+ *   stroke(0);
+ *   ellipse(x, y, 5, 5);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * stroke(255, 102, 0);
+ * steps = 16;
+ * for (i = 0; i <= steps; i++) {
+ *   t = i / steps;
+ *   x = bezierPoint(85, 10, 90, 15, t);
+ *   y = bezierPoint(20, 10, 90, 80, t);
+ *   tx = bezierTangent(85, 10, 90, 15, t);
+ *   ty = bezierTangent(20, 10, 90, 80, t);
+ *   a = atan2(ty, tx);
+ *   a -= HALF_PI;
+ *   line(x, y, cos(a)*8 + x, sin(a)*8 + y);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierTangent = function(a, b, c, d, t) {
+  var adjustedT = 1-t;
+  return 3*d*Math.pow(t,2) -
+   3*c*Math.pow(t,2) +
+   6*c*adjustedT*t -
+   6*b*adjustedT*t +
+   3*b*Math.pow(adjustedT,2) -
+   3*a*Math.pow(adjustedT,2);
+};
+
+/**
+ * Draws a curved line on the screen between two points, given as the
+ * middle four parameters. The first two parameters are a control point, as
+ * if the curve came from this point even though it's not drawn. The last
+ * two parameters similarly describe the other control point. <br /><br />
+ * Longer curves can be created by putting a series of curve() functions
+ * together or using curveVertex(). An additional function called
+ * curveTightness() provides control for the visual quality of the curve.
+ * The curve() function is an implementation of Catmull-Rom splines.
+ *
+ * @method curve
+ * @param  {Number} x1 x-coordinate for the beginning control point
+ * @param  {Number} y1 y-coordinate for the beginning control point
+ * @param  {Number} x2 x-coordinate for the first point
+ * @param  {Number} y2 y-coordinate for the first point
+ * @param  {Number} x3 x-coordinate for the second point
+ * @param  {Number} y3 y-coordinate for the second point
+ * @param  {Number} x4 x-coordinate for the ending control point
+ * @param  {Number} y4 y-coordinate for the ending control point
+ * @return {Object}    the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * stroke(255, 102, 0);
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * stroke(0);
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ * stroke(255, 102, 0);
+ * curve(73, 24, 73, 61, 15, 65, 15, 65);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * // Define the curve points as JavaScript objects
+ * p1 = {x: 5, y: 26}, p2 = {x: 73, y: 24}
+ * p3 = {x: 73, y: 61}, p4 = {x: 15, y: 65}
+ * noFill();
+ * stroke(255, 102, 0);
+ * curve(p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)
+ * stroke(0);
+ * curve(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y)
+ * stroke(255, 102, 0);
+ * curve(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, p4.x, p4.y)
+ * </code>
+ * </div>
+ */
+p5.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'curve',
+    args,
+    [ 'Number', 'Number', 'Number', 'Number',
+      'Number', 'Number', 'Number', 'Number' ]
+  );
+
+  if (!this._renderer._doStroke) {
+    return;
+  }
+  this._renderer.curve(x1, y1, x2, y2, x3, y3, x4, y4);
+  return this;
+};
+
+/**
+ * Sets the resolution at which curves display.
+ *
+ * The default value is 20.
+ *
+ * @param {Number} resolution of the curves
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(204);
+ * curveDetail(20);
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * </code>
+ * </div>
+ */
+p5.prototype.curveDetail = function(d) {
+  curveDetail = d;
+  return this;
+};
+
+/**
+ * Modifies the quality of forms created with curve() and curveVertex().
+ * The parameter tightness determines how the curve fits to the vertex
+ * points. The value 0.0 is the default value for tightness (this value
+ * defines the curves to be Catmull-Rom splines) and the value 1.0 connects
+ * all the points with straight lines. Values within the range -5.0 and 5.0
+ * will deform the curves but will leave them recognizable and as values
+ * increase in magnitude, they will continue to deform.
+ *
+ * @method curveTightness
+ * @param {Number} amount of deformation from the original vertices
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse left and right to see the curve change
+ *
+ * function setup() {
+ *   createCanvas(100, 100);
+ *   noFill();
+ * }
+ *
+ * function draw() {
+ *   background(204);
+ *   var t = map(mouseX, 0, width, -5, 5);
+ *   curveTightness(t);
+ *   beginShape();
+ *   curveVertex(10, 26);
+ *   curveVertex(10, 26);
+ *   curveVertex(83, 24);
+ *   curveVertex(83, 61);
+ *   curveVertex(25, 65);
+ *   curveVertex(25, 65);
+ *   endShape();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.curveTightness = function (t) {
+  this._renderer._curveTightness = t;
+};
+
+/**
+ * Evaluates the curve at position t for points a, b, c, d.
+ * The parameter t varies between 0 and 1, a and d are points
+ * on the curve, and b and c are the control points.
+ * This can be done once with the x coordinates and a second time
+ * with the y coordinates to get the location of a curve at t.
+ *
+ * @method curvePoint
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} bezier value at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ * fill(255);
+ * ellipseMode(CENTER);
+ * steps = 6;
+ * for (i = 0; i <= steps; i++) {
+ *   t = i / steps;
+ *   x = curvePoint(5, 5, 73, 73, t);
+ *   y = curvePoint(26, 26, 24, 61, t);
+ *   ellipse(x, y, 5, 5);
+ *   x = curvePoint(5, 73, 73, 15, t);
+ *   y = curvePoint(26, 24, 61, 65, t);
+ *   ellipse(x, y, 5, 5);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.curvePoint = function(a, b, c, d, t) {
+  var t3 = t*t*t,
+    t2 = t*t,
+    f1 = -0.5 * t3 + t2 - 0.5 * t,
+    f2 = 1.5 * t3 - 2.5 * t2 + 1.0,
+    f3 = -1.5 * t3 + 2.0 * t2 + 0.5 * t,
+    f4 = 0.5 * t3 - 0.5 * t2;
+  return a*f1 + b*f2 + c*f3 + d*f4;
+};
+
+/**
+ * Evaluates the tangent to the curve at position t for points a, b, c, d.
+ * The parameter t varies between 0 and 1, a and d are points on the curve,
+ * and b and c are the control points.
+ *
+ * @method curveTangent
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} the tangent at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ * steps = 6;
+ * for (i = 0; i <= steps; i++) {
+ *   t = i / steps;
+ *   x = curvePoint(5, 73, 73, 15, t);
+ *   y = curvePoint(26, 24, 61, 65, t);
+ *   //ellipse(x, y, 5, 5);
+ *   tx = curveTangent(5, 73, 73, 15, t);
+ *   ty = curveTangent(26, 24, 61, 65, t);
+ *   a = atan2(ty, tx);
+ *   a -= PI/2.0;
+ *   line(x, y, cos(a)*8 + x, sin(a)*8 + y);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.curveTangent = function(a, b,c, d, t) {
+  var t2 = t*t,
+    f1 = (-3*t2)/2 + 2*t - 0.5,
+    f2 = (9*t2)/2 - 5*t,
+    f3 = (-9*t2)/2 + 4*t + 0.5,
+    f4 = (3*t2)/2 - t;
+  return a*f1 + b*f2 + c*f3 + d*f4;
+};
+
+module.exports = p5;
+
+},{"./core":48,"./error_helpers":51}],50:[function(_dereq_,module,exports){
+/**
+ * @module Environment
+ * @submodule Environment
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var C = _dereq_('./constants');
+
+var standardCursors = [C.ARROW, C.CROSS, C.HAND, C.MOVE, C.TEXT, C.WAIT];
+
+p5.prototype._frameRate = 0;
+p5.prototype._lastFrameTime = window.performance.now();
+p5.prototype._targetFrameRate = 60;
+
+
+if (window.console && console.log) {
+  /**
+   * The print() function writes to the console area of your browser.
+   * This function is often helpful for looking at the data a program is
+   * producing. This function creates a new line of text for each call to
+   * the function. Individual elements can be
+   * separated with quotes ("") and joined with the addition operator (+).
+   * <br><br>
+   * While print() is similar to console.log(), it does not directly map to
+   * it in order to simulate easier to understand behavior than
+   * console.log(). Due to this, it is slower. For fastest results, use
+   * console.log().
+   *
+   * @method print
+   * @param {Any} contents any combination of Number, String, Object, Boolean,
+   *                       Array to print
+   * @example
+   * <div><code class='norender'>
+   * var x = 10;
+   * print("The value of x is " + x);
+   * // prints "The value of x is 10"
+   * </code></div>
+   */
+  // Converts passed args into a string and then parses that string to
+  // simulate synchronous behavior. This is a hack and is gross.
+  // Since this will not work on all objects, particularly circular
+  // structures, simply console.log() on error.
+  p5.prototype.print = function(args) {
+    try {
+      var newArgs = JSON.parse(JSON.stringify(args));
+      console.log(newArgs);
+    } catch(err) {
+      console.log(args);
+    }
+  };
+} else {
+  p5.prototype.print = function() {};
+}
+
+p5.prototype.println = p5.prototype.print;
+
+/**
+ * The system variable frameCount contains the number of frames that have
+ * been displayed since the program started. Inside setup() the value is 0,
+ * after the first iteration of draw it is 1, etc.
+ *
+ * @property frameCount
+ * @example
+ *   <div><code>
+ *     function setup() {
+ *       frameRate(30);
+ *       textSize(20);
+ *       textSize(30);
+ *       textAlign(CENTER);
+ *     }
+ *
+ *     function draw() {
+ *       background(200);
+ *       text(frameCount, width/2, height/2);
+ *     }
+ *   </code></div>
+ */
+p5.prototype.frameCount = 0;
+
+/**
+ * Confirms if the window a p5.js program is in is "focused," meaning that
+ * the sketch will accept mouse or keyboard input. This variable is
+ * "true" if the window is focused and "false" if not.
+ *
+ * @property focused
+ * @example
+ * <div><code>
+ * // To demonstrate, put two windows side by side.
+ * // Click on the window that the p5 sketch isn't in!
+ * function draw() {
+ *   if (focused) {  // or "if (focused === true)"
+ *     noStroke();
+ *     fill(0, 200, 0);
+ *     ellipse(25, 25, 50, 50);
+ *   } else {
+ *     stroke(200,0,0);
+ *     line(0, 0, 100, 100);
+ *     line(100, 0, 0, 100);
+ *   }
+ * }
+ *
+ * </code></div>
+ */
+p5.prototype.focused = (document.hasFocus());
+
+/**
+ * Sets the cursor to a predefined symbol or an image, or makes it visible
+ * if already hidden. If you are trying to set an image as the cursor, the
+ * recommended size is 16x16 or 32x32 pixels. It is not possible to load an
+ * image as the cursor if you are exporting your program for the Web, and not
+ * all MODES work with all browsers. The values for parameters x and y must
+ * be less than the dimensions of the image.
+ *
+ * @method cursor
+ * @param {Number/Constant} type either ARROW, CROSS, HAND, MOVE, TEXT, or
+ *                               WAIT, or path for image
+ * @param {Number}          [x]  the horizontal active spot of the cursor
+ * @param {Number}          [y]  the vertical active spot of the cursor
+ * @example
+ * <div><code>
+ * // Move the mouse left and right across the image
+ * // to see the cursor change from a cross to a hand
+ * function draw() {
+ *   line(width/2, 0, width/2, height);
+ *   if (mouseX < 50) {
+ *     cursor(CROSS);
+ *   } else {
+ *     cursor(HAND);
+ *   }
+ * }
+ * </code></div>
+ */
+p5.prototype.cursor = function(type, x, y) {
+  var cursor = 'auto';
+  var canvas = this._curElement.elt;
+  if (standardCursors.indexOf(type) > -1) {
+    // Standard css cursor
+    cursor = type;
+  } else if (typeof type === 'string') {
+    var coords = '';
+    if (x && y && (typeof x === 'number' && typeof y === 'number')) {
+      // Note that x and y values must be unit-less positive integers < 32
+      // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
+      coords = x + ' ' + y;
+    }
+    if (type.substring(0, 6) !== 'http://') {
+      // Image (absolute url)
+      cursor = 'url(' + type + ') ' + coords + ', auto';
+    } else if (/\.(cur|jpg|jpeg|gif|png|CUR|JPG|JPEG|GIF|PNG)$/.test(type)) {
+      // Image file (relative path) - Separated for performance reasons
+      cursor = 'url(' + type + ') ' + coords + ', auto';
+    } else {
+      // Any valid string for the css cursor property
+      cursor = type;
+    }
+  }
+  canvas.style.cursor = cursor;
+};
+
+/**
+ * Specifies the number of frames to be displayed every second. For example,
+ * the function call frameRate(30) will attempt to refresh 30 times a second.
+ * If the processor is not fast enough to maintain the specified rate, the
+ * frame rate will not be achieved. Setting the frame rate within setup() is
+ * recommended. The default rate is 60 frames per second. This is the same as
+ * setFrameRate(val).
+ * <br><br>
+ * Calling frameRate() with no arguments returns the current framerate. This
+ * is the same as getFrameRate().
+ * <br><br>
+ * Calling frameRate() with arguments that are not of the type numbers
+ * or are non positive also returns current framerate.
+ *
+ * @method frameRate
+ * @param  {Number} [fps] number of frames to be displayed every second
+ * @return {Number}       current frameRate
+ * @example
+ *
+ * <div><code>
+ * var rectX = 0;
+ * var fr = 30; //starting FPS
+ * var clr;
+ *
+ * function setup() {
+ *   background(200);
+ *   frameRate(fr); // Attempt to refresh at starting FPS
+ *   clr = color(255,0,0);
+ * }
+ *
+ * function draw() {
+ *   background(200);
+ *   rectX = rectX += 1; // Move Rectangle
+ *
+ *   if (rectX >= width) { // If you go off screen.
+ *     if (fr == 30) {
+ *       clr = color(0,0,255);
+ *       fr = 10;
+ *       frameRate(fr); // make frameRate 10 FPS
+ *     } else {
+ *       clr = color(255,0,0);
+ *       fr = 30;
+ *       frameRate(fr); // make frameRate 30 FPS
+ *     }
+ *     rectX = 0;
+ *   }
+ *   fill(clr);
+ *   rect(rectX, 40, 20,20);
+ * }
+ * </div></code>
+ *
+ */
+p5.prototype.frameRate = function(fps) {
+  if (typeof fps !== 'number' || fps <= 0) {
+    return this._frameRate;
+  } else {
+    this._setProperty('_targetFrameRate', fps);
+    this._runFrames();
+    return this;
+  }
+};
+/**
+ * Returns the current framerate.
+ *
+ * @return {Number} current frameRate
+ */
+p5.prototype.getFrameRate = function() {
+  return this.frameRate();
+};
+
+/**
+ * Specifies the number of frames to be displayed every second. For example,
+ * the function call frameRate(30) will attempt to refresh 30 times a second.
+ * If the processor is not fast enough to maintain the specified rate, the
+ * frame rate will not be achieved. Setting the frame rate within setup() is
+ * recommended. The default rate is 60 frames per second.
+ *
+ * Calling frameRate() with no arguments returns the current framerate.
+ *
+ * @param {Number} [fps] number of frames to be displayed every second
+ */
+p5.prototype.setFrameRate = function(fps) {
+  return this.frameRate(fps);
+};
+
+/**
+ * Hides the cursor from view.
+ *
+ * @method noCursor
+ * @example
+ * <div><code>
+ * function setup() {
+ *   noCursor();
+ * }
+ *
+ * function draw() {
+ *   background(200);
+ *   ellipse(mouseX, mouseY, 10, 10);
+ * }
+ * </code></div>
+ */
+p5.prototype.noCursor = function() {
+  this._curElement.elt.style.cursor = 'none';
+};
+
+
+/**
+ * System variable that stores the width of the entire screen display. This
+ * is used to run a full-screen program on any display size.
+ *
+ * @property displayWidth
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(displayWidth, displayHeight);
+ * </code></div>
+ */
+p5.prototype.displayWidth = screen.width;
+
+/**
+ * System variable that stores the height of the entire screen display. This
+ * is used to run a full-screen program on any display size.
+ *
+ * @property displayHeight
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(displayWidth, displayHeight);
+ * </code></div>
+ */
+p5.prototype.displayHeight = screen.height;
+
+/**
+ * System variable that stores the width of the inner window, it maps to
+ * window.innerWidth.
+ *
+ * @property windowWidth
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(windowWidth, windowHeight);
+ * </code></div>
+ */
+p5.prototype.windowWidth = window.innerWidth;
+/**
+ * System variable that stores the height of the inner window, it maps to
+ * window.innerHeight.
+ *
+ * @property windowHeight
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(windowWidth, windowHeight);
+ * </code></div>
+ */
+p5.prototype.windowHeight = window.innerHeight;
+
+/**
+ * The windowResized() function is called once every time the browser window
+ * is resized. This is a good place to resize the canvas or do any other
+ * adjustements to accomodate the new window size.
+ *
+ * @method windowResized
+ * @example
+ * <div class="norender"><code>
+ * function setup() {
+ *   createCanvas(windowWidth, windowHeight);
+ * }
+ *
+ * function draw() {
+ *  background(0, 100, 200);
+ * }
+ *
+ * function windowResized() {
+ *   resizeCanvas(windowWidth, windowHeight);
+ * }
+ * </code></div>
+ */
+p5.prototype._onresize = function(e){
+  this._setProperty('windowWidth', window.innerWidth);
+  this._setProperty('windowHeight', window.innerHeight);
+  var context = this._isGlobal ? window : this;
+  var executeDefault;
+  if (typeof context.windowResized === 'function') {
+    executeDefault = context.windowResized(e);
+    if (executeDefault !== undefined && !executeDefault) {
+      e.preventDefault();
+    }
+  }
+};
+
+/**
+ * System variable that stores the width of the drawing canvas. This value
+ * is set by the first parameter of the createCanvas() function.
+ * For example, the function call createCanvas(320, 240) sets the width
+ * variable to the value 320. The value of width defaults to 100 if
+ * createCanvas() is not used in a program.
+ *
+ * @property width
+ */
+p5.prototype.width = 0;
+
+/**
+ * System variable that stores the height of the drawing canvas. This value
+ * is set by the second parameter of the createCanvas() function. For
+ * example, the function call createCanvas(320, 240) sets the height
+ * variable to the value 240. The value of height defaults to 100 if
+ * createCanvas() is not used in a program.
+ *
+ * @property height
+ */
+p5.prototype.height = 0;
+
+/**
+ * If argument is given, sets the sketch to fullscreen or not based on the
+ * value of the argument. If no argument is given, returns the current
+ * fullscreen state. Note that due to browser restrictions this can only
+ * be called on user input, for example, on mouse press like the example
+ * below.
+ *
+ * @method fullscreen
+ * @param  {Boolean} [val] whether the sketch should be in fullscreen mode
+ * or not
+ * @return {Boolean} current fullscreen state
+ * @example
+ * <div>
+ * <code>
+ * // Clicking in the box toggles fullscreen on and off.
+ * function setup() {
+ *   background(200);
+ * }
+ * function mousePressed() {
+ *   if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ *     var fs = fullscreen();
+ *     fullscreen(!fs);
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.fullscreen = function(val) {
+  // no arguments, return fullscreen or not
+  if (typeof val === 'undefined') {
+    return document.fullscreenElement ||
+           document.webkitFullscreenElement ||
+           document.mozFullScreenElement ||
+           document.msFullscreenElement;
+  } else { // otherwise set to fullscreen or not
+    if (val) {
+      launchFullscreen(document.documentElement);
+    } else {
+      exitFullscreen();
+    }
+  }
+};
+
+/**
+ * Sets the pixel scaling for high pixel density displays. By default
+ * pixel density is set to match display density, call pixelDensity(1)
+ * to turn this off. Calling pixelDensity() with no arguments returns
+ * the current pixel density of the sketch.
+ *
+ *
+ * @method pixelDensity
+ * @param  {Number} [val] whether or how much the sketch should scale
+ * @returns {Number} current pixel density of the sketch
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   pixelDensity(1);
+ *   createCanvas(100, 100);
+ *   background(200);
+ *   ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * function setup() {
+ *   pixelDensity(3.0);
+ *   createCanvas(100, 100);
+ *   background(200);
+ *   ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.pixelDensity = function(val) {
+  if (typeof val === 'number') {
+    this._pixelDensity = val;
+  } else {
+    return this._pixelDensity;
+  }
+  this.resizeCanvas(this.width, this.height, true);
+};
+
+/**
+ * Returns the pixel density of the current display the sketch is running on.
+ *
+ * @method displayDensity
+ * @returns {Number} current pixel density of the display
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   var density = displayDensity();
+ *   pixelDensity(density);
+ *   createCanvas(100, 100);
+ *   background(200);
+ *   ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.displayDensity = function() {
+  return window.devicePixelRatio;
+};
+
+function launchFullscreen(element) {
+  var enabled = document.fullscreenEnabled ||
+                document.webkitFullscreenEnabled ||
+                document.mozFullScreenEnabled ||
+                document.msFullscreenEnabled;
+  if (!enabled) {
+    throw new Error('Fullscreen not enabled in this browser.');
+  }
+  if(element.requestFullscreen) {
+    element.requestFullscreen();
+  } else if(element.mozRequestFullScreen) {
+    element.mozRequestFullScreen();
+  } else if(element.webkitRequestFullscreen) {
+    element.webkitRequestFullscreen();
+  } else if(element.msRequestFullscreen) {
+    element.msRequestFullscreen();
+  }
+}
+
+function exitFullscreen() {
+  if(document.exitFullscreen) {
+    document.exitFullscreen();
+  } else if(document.mozCancelFullScreen) {
+    document.mozCancelFullScreen();
+  } else if(document.webkitExitFullscreen) {
+    document.webkitExitFullscreen();
+  } else if (document.msExitFullscreen) {
+    document.msExitFullscreen();
+  }
+}
+
+
+/**
+ * Gets the current URL.
+ * @method getURL
+ * @return {String} url
+ * @example
+ * <div>
+ * <code>
+ * var url;
+ * var x = 100;
+ *
+ * function setup() {
+ *   fill(0);
+ *   noStroke();
+ *   url = getURL();
+ * }
+ *
+ * function draw() {
+ *   background(200);
+ *   text(url, x, height/2);
+ *   x--;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.getURL = function() {
+  return location.href;
+};
+/**
+ * Gets the current URL path as an array.
+ * @method getURLPath
+ * @return {Array} path components
+ * @example
+ * <div class='norender'><code>
+ * function setup() {
+ *   var urlPath = getURLPath();
+ *   for (var i=0; i&lt;urlPath.length; i++) {
+ *     text(urlPath[i], 10, i*20+20);
+ *   }
+ * }
+ * </code></div>
+ */
+p5.prototype.getURLPath = function() {
+  return location.pathname.split('/').filter(function(v){return v!=='';});
+};
+/**
+ * Gets the current URL params as an Object.
+ * @method getURLParams
+ * @return {Object} URL params
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // Example: http://p5js.org?year=2014&month=May&day=15
+ *
+ * function setup() {
+ *   var params = getURLParams();
+ *   text(params.day, 10, 20);
+ *   text(params.month, 10, 40);
+ *   text(params.year, 10, 60);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.getURLParams = function() {
+  var re = /[?&]([^&=]+)(?:[&=])([^&=]+)/gim;
+  var m;
+  var v={};
+  while ((m = re.exec(location.search)) != null) {
+    if (m.index === re.lastIndex) {
+      re.lastIndex++;
+    }
+    v[m[1]]=m[2];
+  }
+  return v;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],51:[function(_dereq_,module,exports){
+/**
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var doFriendlyWelcome = false; // TEMP until we get it all working LM
+
+// -- Borrowed from jQuery 1.11.3 --
+var class2type = {};
+var toString = class2type.toString;
+var names = ['Boolean', 'Number', 'String', 'Function',
+             'Array', 'Date', 'RegExp', 'Object', 'Error'];
+for (var n=0; n<names.length; n++) {
+  class2type[ '[object ' + names[n] + ']' ] = names[n].toLowerCase();
+}
+var getType = function( obj ) {
+  if ( obj == null ) {
+    return obj + '';
+  }
+  return typeof obj === 'object' || typeof obj === 'function' ?
+    class2type[ toString.call(obj) ] || 'object' :
+    typeof obj;
+};
+var isArray = Array.isArray || function( obj ) {
+  return getType(obj) === 'array';
+};
+var isNumeric =function( obj ) {
+  // parseFloat NaNs numeric-cast false positives (null|true|false|"")
+  // ...but misinterprets leading-number strings, particularly hex literals
+  // subtraction forces infinities to NaN
+  // adding 1 corrects loss of precision from parseFloat (#15100)
+  return !isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
+};
+// -- End borrow --
+
+/**
+ * Checks the definition type against the argument type
+ * If any of these passes (in order), it matches:
+ *
+ * - p5.* definitions are checked with instanceof
+ * - Booleans are let through (everything is truthy or falsey)
+ * - Lowercase of the definition is checked against the js type
+ * - Number types are checked to see if they are numerically castable
+ */
+var numberTypes = ['Number', 'Integer', 'Number/Constant'];
+function typeMatches(defType, argType, arg) {
+  if(defType.match(/^p5\./)) {
+    var parts = defType.split('.');
+    return arg instanceof p5[parts[1]];
+  }
+  return defType === 'Boolean' || // Anything is truthy, cover in Debug Guide
+    (defType.toLowerCase() === argType) ||
+    (numberTypes.indexOf(defType) > -1 && isNumeric(arg));
+}
+
+/**
+ * Prints out a fancy, colorful message to the console log
+ *
+ * @param  {String}               message the words to be said
+ * @param  {String}               func    the name of the function to link
+ * @param  {Integer/Color String} color   CSS color string or error type
+ *
+ * @return console logs
+ */
+// Wrong number of params, undefined param, wrong type
+var PARAM_COUNT = 0;
+var EMPTY_VAR = 1;
+var WRONG_TYPE = 2;
+var FILE_LOAD = 3;
+// p5.js blue, p5.js orange, auto dark green; fallback p5.js darkened magenta
+// See testColors below for all the color codes and names
+var typeColors = ['#2D7BB6', '#EE9900', '#4DB200', '#C83C00'];
+function report(message, func, color) {
+  if(doFriendlyWelcome){
+    friendlyWelcome();
+    doFriendlyWelcome =false;
+  }
+  if ('undefined' === getType(color)) {
+    color   = '#B40033'; // dark magenta
+  } else if (getType(color) === 'number') { // Type to color
+    color = typeColors[color];
+  }
+  // LM TEMP commenting this out until we get the whole system working
+  // if (func.substring(0,4) === 'load'){
+  //   console.log(
+  //     '%c> p5.js says: '+message+'%c'+
+  //     '[https://github.com/processing/p5.js/wiki/Local-server]',
+  //     'background-color:' + color + ';color:#FFF;',
+  //     'background-color:transparent;color:' + color +';',
+  //     'background-color:' + color + ';color:#FFF;',
+  //     'background-color:transparent;color:' + color +';'
+  //   );
+  // }
+  // else{
+  //   console.log(
+  //     '%c> p5.js says: '+message+'%c [http://p5js.org/reference/#p5/'+func+
+  //     ']', 'background-color:' + color + ';color:#FFF;',
+  //     'background-color:transparent;color:' + color +';'
+  //   );
+  // }
+}
+
+/**
+ * Validate all the parameters of a function for number and type
+ * NOTE THIS FUNCTION IS TEMPORARILY DISABLED UNTIL FURTHER WORK
+ * AND UPDATES ARE IMPLEMENTED. -LMCCART
+ *
+ * @param  {String} func  name of function we're checking
+ * @param  {Array}  args  pass of the JS default arguments array
+ * @param  {Array}  types List of types accepted ['Number', 'String, ...] OR
+ *                        a list of lists for each format: [
+ *                          ['String', 'Number', 'Number'],
+ *                          ['String', 'Number', 'Number', 'Number', 'Number'
+ *                        ]
+ *
+ * @return console logs
+ */
+p5.prototype._validateParameters = function(func, args, types) {
+  if (!isArray(types[0])) {
+    types = [types];
+  }
+  // Check number of parameters
+  // Example: "You wrote ellipse(X,X,X). ellipse was expecting 4
+  //          parameters. Try ellipse(X,X,X,X)."
+  var diff = Math.abs(args.length-types[0].length);
+  var message, tindex = 0;
+  for (var i=1, len=types.length; i<len; i++) {
+    var d = Math.abs(args.length-types[i].length);
+    if (d <= diff) {
+      tindex = i;
+      diff = d;
+    }
+  }
+  var symbol = 'X'; // Parameter placeholder
+  if(diff > 0) {
+    message = 'You wrote ' + func + '(';
+    // Concat an appropriate number of placeholders for call
+    if (args.length > 0) {
+      message += symbol + Array(args.length).join(',' + symbol);
+    }
+    message += '). ' + func + ' was expecting ' + types[tindex].length +
+      ' parameters. Try ' + func + '(';
+    // Concat an appropriate number of placeholders for definition
+    if (types[tindex].length > 0) {
+      message += symbol + Array(types[tindex].length).join(',' + symbol);
+    }
+    message += ').';
+    // If multiple definitions
+    if (types.length > 1) {
+      message += ' ' + func + ' takes different numbers of parameters ' +
+        'depending on what you want to do. Click this link to learn more: ';
+    }
+    report(message, func, PARAM_COUNT);
+  }
+  // Type checking
+  // Example: "It looks like ellipse received an empty variable in spot #2."
+  // Example: "ellipse was expecting a number for parameter #1,
+  //           received "foo" instead."
+  for (var format=0; format<types.length; format++) {
+    for (var p=0; p < types[format].length && p < args.length; p++) {
+      var defType = types[format][p];
+      var argType = getType(args[p]);
+      if ('undefined' === argType || null === argType) {
+        report('It looks like ' + func +
+          ' received an empty variable in spot #' + (p+1) +
+          '. If not intentional, this is often a problem with scope: ' +
+          '[link to scope].', func, EMPTY_VAR);
+      } else if (defType !== '*' && !typeMatches(defType, argType, args[p])) {
+        message = func + ' was expecting a ' + defType.toLowerCase() +
+          ' for parameter #' + (p+1) + ', received ';
+        // Wrap strings in quotes
+        message += 'string' === argType ? '"' + args[p] + '"' : args[p];
+        message += ' instead.';
+        // If multiple definitions
+        if (types.length > 1) {
+          message += ' ' + func + ' takes different numbers of parameters ' +
+            'depending on what you want to do. ' +
+            'Click this link to learn more:';
+        }
+        report(message, func, WRONG_TYPE);
+      }
+    }
+  }
+};
+/*
+ * NOTE THIS FUNCTION IS TEMPORARILY DISABLED UNTIL FURTHER WORK
+ * AND UPDATES ARE IMPLEMENTED. -LMCCART
+ */
+p5.prototype._validateParameters = function() {
+  return true;
+};
+
+var errorCases = {
+  '0': {
+    fileType: 'image',
+    method: 'loadImage',
+    message: ' hosting the image online,'
+  },
+  '1': {
+    fileType: 'XML file',
+    method: 'loadXML'
+  },
+  '2': {
+    fileType: 'table file',
+    method: 'loadTable'
+  },
+  '3': {
+    fileType: 'text file',
+    method: 'loadStrings'
+  }
+};
+p5._friendlyFileLoadError = function (errorType, filePath) {
+  var errorInfo = errorCases[ errorType ];
+  var message = 'It looks like there was a problem' +
+  ' loading your ' + errorInfo.fileType + '.' +
+  ' Try checking if the file path%c [' + filePath + '] %cis correct,' +
+  (errorInfo.message || '') + ' or running a local server.';
+  report(message, errorInfo.method, FILE_LOAD);
+};
+
+function friendlyWelcome() {
+  // p5.js brand - magenta: #ED225D
+  var astrixBgColor = 'transparent';
+  var astrixTxtColor = '#ED225D';
+  var welcomeBgColor = '#ED225D';
+  var welcomeTextColor = 'white';
+  console.log(
+  '%c    _ \n'+
+  ' /\\| |/\\ \n'+
+  ' \\ ` \' /  \n'+
+  ' / , . \\  \n'+
+  ' \\/|_|\\/ '+
+  '\n\n%c> p5.js says: Welcome! '+
+  'This is your friendly debugger. ' +
+  'To turn me off switch to using “p5.min.js”.',
+  'background-color:'+astrixBgColor+';color:' + astrixTxtColor +';',
+  'background-color:'+welcomeBgColor+';color:' + welcomeTextColor +';'
+  );
+}
+
+/**
+ * Prints out all the colors in the color pallete with white text.
+ * For color blindness testing.
+ */
+/* function testColors() {
+  var str = 'A box of biscuits, a box of mixed biscuits and a biscuit mixer';
+  report(str, 'println', '#ED225D'); // p5.js magenta
+  report(str, 'println', '#2D7BB6'); // p5.js blue
+  report(str, 'println', '#EE9900'); // p5.js orange
+  report(str, 'println', '#A67F59'); // p5.js light brown
+  report(str, 'println', '#704F21'); // p5.js gold
+  report(str, 'println', '#1CC581'); // auto cyan
+  report(str, 'println', '#FF6625'); // auto orange
+  report(str, 'println', '#79EB22'); // auto green
+  report(str, 'println', '#B40033'); // p5.js darkened magenta
+  report(str, 'println', '#084B7F'); // p5.js darkened blue
+  report(str, 'println', '#945F00'); // p5.js darkened orange
+  report(str, 'println', '#6B441D'); // p5.js darkened brown
+  report(str, 'println', '#2E1B00'); // p5.js darkened gold
+  report(str, 'println', '#008851'); // auto dark cyan
+  report(str, 'println', '#C83C00'); // auto dark orange
+  report(str, 'println', '#4DB200'); // auto dark green
+} */
+
+// This is a lazily-defined list of p5 symbols that may be
+// misused by beginners at top-level code, outside of setup/draw. We'd like
+// to detect these errors and help the user by suggesting they move them
+// into setup/draw.
+//
+// For more details, see https://github.com/processing/p5.js/issues/1121.
+var misusedAtTopLevelCode = null;
+var FAQ_URL = 'https://github.com/processing/p5.js/wiki/' +
+              'Frequently-Asked-Questions' +
+              '#why-cant-i-assign-variables-using-p5-functions-and-' +
+              'variables-before-setup';
+
+function defineMisusedAtTopLevelCode() {
+  var uniqueNamesFound = {};
+
+  var getSymbols = function(obj) {
+    return Object.getOwnPropertyNames(obj).filter(function(name) {
+      if (name[0] === '_') {
+        return false;
+      }
+      if (name in uniqueNamesFound) {
+        return false;
+      }
+
+      uniqueNamesFound[name] = true;
+
+      return true;
+    }).map(function(name) {
+      var type;
+
+      if (typeof(obj[name]) === 'function') {
+        type = 'function';
+      } else if (name === name.toUpperCase()) {
+        type = 'constant';
+      } else {
+        type = 'variable';
+      }
+
+      return {name: name, type: type};
+    });
+  };
+
+  misusedAtTopLevelCode = [].concat(
+    getSymbols(p5.prototype),
+    // At present, p5 only adds its constants to p5.prototype during
+    // construction, which may not have happened at the time a
+    // ReferenceError is thrown, so we'll manually add them to our list.
+    getSymbols(_dereq_('./constants'))
+  );
+
+  // This will ultimately ensure that we report the most specific error
+  // possible to the user, e.g. advising them about HALF_PI instead of PI
+  // when their code misuses the former.
+  misusedAtTopLevelCode.sort(function(a, b) {
+    return b.name.length - a.name.length;
+  });
+}
+
+function helpForMisusedAtTopLevelCode(e, log) {
+  if (!log) {
+    log = console.log.bind(console);
+  }
+
+  if (!misusedAtTopLevelCode) {
+    defineMisusedAtTopLevelCode();
+  }
+
+  // If we find that we're logging lots of false positives, we can
+  // uncomment the following code to avoid displaying anything if the
+  // user's code isn't likely to be using p5's global mode. (Note that
+  // setup/draw are more likely to be defined due to JS function hoisting.)
+  //
+  //if (!('setup' in window || 'draw' in window)) {
+  //  return;
+  //}
+
+  misusedAtTopLevelCode.some(function(symbol) {
+    // Note that while just checking for the occurrence of the
+    // symbol name in the error message could result in false positives,
+    // a more rigorous test is difficult because different browsers
+    // log different messages, and the format of those messages may
+    // change over time.
+    //
+    // For example, if the user uses 'PI' in their code, it may result
+    // in any one of the following messages:
+    //
+    //   * 'PI' is undefined                           (Microsoft Edge)
+    //   * ReferenceError: PI is undefined             (Firefox)
+    //   * Uncaught ReferenceError: PI is not defined  (Chrome)
+
+    if (e.message && e.message.indexOf(symbol.name) !== -1) {
+      log('%cDid you just try to use p5.js\'s ' + symbol.name +
+          (symbol.type === 'function' ? '() ' : ' ') + symbol.type +
+          '? If so, you may want to ' +
+          'move it into your sketch\'s setup() function.\n\n' +
+          'For more details, see: ' + FAQ_URL,
+          'color: #B40033' /* Dark magenta */);
+      return true;
+    }
+  });
+}
+
+// Exposing this primarily for unit testing.
+p5.prototype._helpForMisusedAtTopLevelCode = helpForMisusedAtTopLevelCode;
+
+if (document.readyState !== 'complete') {
+  window.addEventListener('error', helpForMisusedAtTopLevelCode, false);
+
+  // Our job is only to catch ReferenceErrors that are thrown when
+  // global (non-instance mode) p5 APIs are used at the top-level
+  // scope of a file, so we'll unbind our error listener now to make
+  // sure we don't log false positives later.
+  window.addEventListener('load', function() {
+    window.removeEventListener('error', helpForMisusedAtTopLevelCode, false);
+  });
+}
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],52:[function(_dereq_,module,exports){
+/**
+ * @module DOM
+ * @submodule DOM
+ * @for p5.Element
+ */
+
+var p5 = _dereq_('./core');
+
+/**
+ * Base class for all elements added to a sketch, including canvas,
+ * graphics buffers, and other HTML elements. Methods in blue are
+ * included in the core functionality, methods in brown are added
+ * with the <a href="http://p5js.org/libraries/">p5.dom library</a>.
+ * It is not called directly, but p5.Element
+ * objects are created by calling createCanvas, createGraphics,
+ * or in the p5.dom library, createDiv, createImg, createInput, etc.
+ *
+ * @class p5.Element
+ * @constructor
+ * @param {String} elt DOM node that is wrapped
+ * @param {Object} [pInst] pointer to p5 instance
+ */
+p5.Element = function(elt, pInst) {
+  /**
+   * Underlying HTML element. All normal HTML methods can be called on this.
+   *
+   * @property elt
+   */
+  this.elt = elt;
+  this._pInst = pInst;
+  this._events = {};
+  this.width = this.elt.offsetWidth;
+  this.height = this.elt.offsetHeight;
+};
+
+/**
+ *
+ * Attaches the element to the parent specified. A way of setting
+ * the container for the element. Accepts either a string ID, DOM
+ * node, or p5.Element. If no arguments given, parent node is returned.
+ *
+ * @method parent
+ * @param  {String|Object} parent the ID, DOM node, or p5.Element
+ *                         of desired parent element
+ * @return {p5.Element}
+ * @example
+ * <div class="norender"><code>
+ * // in the html file:
+ * &lt;div id="myContainer">&lt;/div>
+ * // in the js file:
+ * var cnv = createCanvas(100, 100);
+ * cnv.parent("myContainer");
+ * </code></div>
+ * <div class='norender'><code>
+ * var div0 = createDiv('this is the parent');
+ * var div1 = createDiv('this is the child');
+ * div1.parent(div0); // use p5.Element
+ * </code></div>
+ * <div class='norender'><code>
+ * var div0 = createDiv('this is the parent');
+ * div0.id('apples');
+ * var div1 = createDiv('this is the child');
+ * div1.parent('apples'); // use id
+ * </code></div>
+ * <div class='norender'><code>
+ * var elt = document.getElementById('myParentDiv');
+ * var div1 = createDiv('this is the child');
+ * div1.parent(elt); // use element from page
+ * </code></div>
+ */
+p5.Element.prototype.parent = function(p) {
+  if (arguments.length === 0){
+    return this.elt.parentNode;
+  } else {
+    if (typeof p === 'string') {
+      if (p[0] === '#') {
+        p = p.substring(1);
+      }
+      p = document.getElementById(p);
+    } else if (p instanceof p5.Element) {
+      p = p.elt;
+    }
+    p.appendChild(this.elt);
+    return this;
+  }
+};
+
+/**
+ *
+ * Sets the ID of the element
+ *
+ * @method id
+ * @param  {String} id ID of the element
+ * @return {p5.Element}
+ */
+p5.Element.prototype.id = function(id) {
+  if (arguments.length === 0) {
+    return this.elt.id;
+  } else {
+    this.elt.id = id;
+    this.width = this.elt.offsetWidth;
+    this.height = this.elt.offsetHeight;
+    return this;
+  }
+};
+
+/**
+ *
+ * Adds given class to the element
+ *
+ * @method class
+ * @param  {String} class class to add
+ * @return {p5.Element}
+ */
+p5.Element.prototype.class = function(c) {
+  if (arguments.length === 0) {
+    return this.elt.className;
+  } else {
+    this.elt.className = c;
+    this.width = this.elt.offsetWidth;
+    this.height = this.elt.offsetHeight;
+    return this;
+  }
+};
+
+/**
+ * The .mousePressed() function is called once after every time a
+ * mouse button is pressed over the element. This can be used to
+ * attach element specific event listeners.
+ *
+ * @method mousePressed
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    pressed over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.mousePressed(changeGray); // attach listener for
+ *                                 // canvas click only
+ *   d = 10;
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with any click anywhere
+ * function mousePressed() {
+ *   d = d + 10;
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ *   g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mousePressed = function (fxn) {
+  attachListener('mousedown', fxn, this);
+  attachListener('touchstart', fxn, this);
+  return this;
+};
+
+/**
+ * The .mouseWheel() function is called once after every time a
+ * mouse wheel is scrolled over the element. This can be used to
+ * attach element specific event listeners.<br><br>
+ * The event.wheelDelta or event.detail property returns negative values if
+ * the mouse wheel if rotated up or away from the user and positive in the
+ * other direction. On OS X with "natural" scrolling enabled, the values are
+ * opposite.
+ *
+ * @method mouseWheel
+ * @param  {Function} fxn function to be fired when mouse wheel is
+ *                    scrolled over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.mouseWheel(changeSize); // attach listener for
+ *                               // activity on canvas only
+ *   d = 10;
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with mousewheel movement
+ * // anywhere on screen
+ * function mouseWheel() {
+ *   g = g + 10;
+ * }
+ *
+ * // this function fires with mousewheel movement
+ * // over canvas only
+ * function changeSize() {
+ *   if (event.wheelDelta > 0) {
+ *     d = d + 10;
+ *   } else {
+ *     d = d - 10;
+ *   }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseWheel = function (fxn) {
+  attachListener('wheel', fxn, this);
+  return this;
+};
+
+/**
+ * The .mouseReleased() function is called once after every time a
+ * mouse button is released over the element. This can be used to
+ * attach element specific event listeners.
+ *
+ * @method mouseReleased
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    released over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.mouseReleased(changeGray); // attach listener for
+ *                                  // activity on canvas only
+ *   d = 10;
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // released
+ * function mouseReleased() {
+ *   d = d + 10;
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // released while on canvas
+ * function changeGray() {
+ *   g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseReleased = function (fxn) {
+  attachListener('mouseup', fxn, this);
+  attachListener('touchend', fxn, this);
+  return this;
+};
+
+
+/**
+ * The .mouseClicked() function is called once after a mouse button is
+ * pressed and released over the element. This can be used to
+ * attach element specific event listeners.
+ *
+ * @method mouseClicked
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    clicked over the element.
+ * @return {p5.Element}
+ * @example
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.mouseClicked(changeGray); // attach listener for
+ *                                 // activity on canvas only
+ *   d = 10;
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // clicked anywhere
+ * function mouseClicked() {
+ *   d = d + 10;
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // clicked on canvas
+ * function changeGray() {
+ *   g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseClicked = function (fxn) {
+  attachListener('click', fxn, this);
+  return this;
+};
+
+/**
+ * The .mouseMoved() function is called once every time a
+ * mouse moves over the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method mouseMoved
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    moved over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d = 30;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.mouseMoved(changeSize); // attach listener for
+ *                               // activity on canvas only
+ *   d = 10;
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ *   fill(200);
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires when mouse moves anywhere on
+ * // page
+ * function mouseMoved() {
+ *   g = g + 5;
+ *   if (g > 255) {
+ *     g = 0;
+ *   }
+ * }
+ *
+ * // this function fires when mouse moves over canvas
+ * function changeSize() {
+ *   d = d + 2;
+ *   if (d > 100) {
+ *     d = 0;
+ *   }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseMoved = function (fxn) {
+  attachListener('mousemove', fxn, this);
+  attachListener('touchmove', fxn, this);
+  return this;
+};
+
+/**
+ * The .mouseOver() function is called once after every time a
+ * mouse moves onto the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method mouseOver
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    moved over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.mouseOver(changeGray);
+ *   d = 10;
+ * }
+ *
+ * function draw() {
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * function changeGray() {
+ *   d = d + 10;
+ *   if (d > 100) {
+ *     d = 0;
+ *   }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseOver = function (fxn) {
+  attachListener('mouseover', fxn, this);
+  return this;
+};
+
+
+/**
+ * The .changed() function is called when the value of an
+ * element is changed.
+ * This can be used to attach an element specific event listener.
+ *
+ * @method changed
+ * @param  {Function} fxn function to be fired when the value of an
+ * element changes.
+ * @return {p5.Element}
+ * @example
+ * <div><code>
+ * var sel;
+ *
+ * function setup() {
+ *   textAlign(CENTER);
+ *   background(200);
+ *   sel = createSelect();
+ *   sel.position(10, 10);
+ *   sel.option('pear');
+ *   sel.option('kiwi');
+ *   sel.option('grape');
+ *   sel.changed(mySelectEvent);
+ * }
+ *
+ * function mySelectEvent() {
+ *   var item = sel.value();
+ *   background(200);
+ *   text("it's a "+item+"!", 50, 50);
+ * }
+ * </code></div>
+ * <div><code>
+ * var checkbox;
+ * var cnv;
+ *
+ * function setup() {
+ *   checkbox = createCheckbox(" fill");
+ *   checkbox.changed(changeFill);
+ *   cnv = createCanvas(100, 100);
+ *   cnv.position(0, 30);
+ *   noFill();
+ * }
+ *
+ * function draw() {
+ *   background(200);
+ *   ellipse(50, 50, 50, 50);
+ * }
+ *
+ * function changeFill() {
+ *   if (checkbox.checked()) {
+ *     fill(0);
+ *   } else {
+ *     noFill();
+ *   }
+ * }
+ * </code></div>
+ */
+p5.Element.prototype.changed = function (fxn) {
+  attachListener('change', fxn, this);
+  return this;
+};
+
+/**
+ * The .input() function is called when any user input is
+ * detected with an element. The input event is often used
+ * to detect keystrokes in a input element, or changes on a
+ * slider element. This can be used to attach an element specific
+ * event listener.
+ *
+ * @method input
+ * @param  {Function} fxn function to be fired on user input.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * // Open your console to see the output
+ * function setup() {
+ *   var inp = createInput('');
+ *   inp.input(myInputEvent);
+ * }
+ *
+ * function myInputEvent() {
+ *   console.log('you are typing: ', this.value());
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.input = function (fxn) {
+  attachListener('input', fxn, this);
+  return this;
+};
+
+/**
+ * The .mouseOut() function is called once after every time a
+ * mouse moves off the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method mouseOut
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    moved off the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.mouseOut(changeGray);
+ *   d = 10;
+ * }
+ *
+ * function draw() {
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * function changeGray() {
+ *   d = d + 10;
+ *   if (d > 100) {
+ *     d = 0;
+ *   }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseOut = function (fxn) {
+  attachListener('mouseout', fxn, this);
+  return this;
+};
+
+/**
+ * The .touchStarted() function is called once after every time a touch is
+ * registered. This can be used to attach element specific event listeners.
+ *
+ * @method touchStarted
+ * @param  {Function} fxn function to be fired when touch is
+ *                    started over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.touchStarted(changeGray); // attach listener for
+ *                                 // canvas click only
+ *   d = 10;
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with any touch anywhere
+ * function touchStarted() {
+ *   d = d + 10;
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ *   g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.touchStarted = function (fxn) {
+  attachListener('touchstart', fxn, this);
+  attachListener('mousedown', fxn, this);
+  return this;
+};
+
+/**
+ * The .touchMoved() function is called once after every time a touch move is
+ * registered. This can be used to attach element specific event listeners.
+ *
+ * @method touchMoved
+ * @param  {Function} fxn function to be fired when touch is moved
+ *                    over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.touchMoved(changeGray); // attach listener for
+ *                               // canvas click only
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ *   g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.touchMoved = function (fxn) {
+  attachListener('touchmove', fxn, this);
+  attachListener('mousemove', fxn, this);
+  return this;
+};
+
+/**
+ * The .touchEnded() function is called once after every time a touch is
+ * registered. This can be used to attach element specific event listeners.
+ *
+ * @method touchEnded
+ * @param  {Function} fxn function to be fired when touch is
+ *                    ended over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ *   cnv = createCanvas(100, 100);
+ *   cnv.touchEnded(changeGray);   // attach listener for
+ *                                 // canvas click only
+ *   d = 10;
+ *   g = 100;
+ * }
+ *
+ * function draw() {
+ *   background(g);
+ *   ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with any touch anywhere
+ * function touchEnded() {
+ *   d = d + 10;
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ *   g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.touchEnded = function (fxn) {
+  attachListener('touchend', fxn, this);
+  attachListener('mouseup', fxn, this);
+  return this;
+};
+
+
+
+/**
+ * The .dragOver() function is called once after every time a
+ * file is dragged over the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method dragOver
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    dragged over the element.
+ * @return {p5.Element}
+ */
+p5.Element.prototype.dragOver = function (fxn) {
+  attachListener('dragover', fxn, this);
+  return this;
+};
+
+/**
+ * The .dragLeave() function is called once after every time a
+ * dragged file leaves the element area. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method dragLeave
+ * @param  {Function} fxn function to be fired when mouse is
+ *                    dragged over the element.
+ * @return {p5.Element}
+ */
+p5.Element.prototype.dragLeave = function (fxn) {
+  attachListener('dragleave', fxn, this);
+  return this;
+};
+
+/**
+ * The .drop() function is called for each file dropped on the element.
+ * It requires a callback that is passed a p5.File object.  You can
+ * optionally pass two callbacks, the first one (required) is triggered
+ * for each file dropped when the file is loaded.  The second (optional)
+ * is triggered just once when a file (or files) are dropped.
+ *
+ * @method drop
+ * @param  {Function} callback triggered when files are dropped.
+ * @param  {Function} callback to receive loaded file.
+ * @return {p5.Element}
+ */
+p5.Element.prototype.drop = function (callback, fxn) {
+  // Make a file loader callback and trigger user's callback
+  function makeLoader(theFile) {
+    // Making a p5.File object
+    var p5file = new p5.File(theFile);
+    return function(e) {
+      p5file.data = e.target.result;
+      callback(p5file);
+    };
+  }
+
+  // Is the file stuff supported?
+  if (window.File && window.FileReader && window.FileList && window.Blob) {
+
+    // If you want to be able to drop you've got to turn off
+    // a lot of default behavior
+    attachListener('dragover',function(evt) {
+      evt.stopPropagation();
+      evt.preventDefault();
+    },this);
+
+    // If this is a drag area we need to turn off the default behavior
+    attachListener('dragleave',function(evt) {
+      evt.stopPropagation();
+      evt.preventDefault();
+    },this);
+
+    // If just one argument it's the callback for the files
+    if (arguments.length > 1) {
+      attachListener('drop', fxn, this);
+    }
+
+    // Deal with the files
+    attachListener('drop', function(evt) {
+
+      evt.stopPropagation();
+      evt.preventDefault();
+
+      // A FileList
+      var files = evt.dataTransfer.files;
+
+      // Load each one and trigger the callback
+      for (var i = 0; i < files.length; i++) {
+        var f = files[i];
+        var reader = new FileReader();
+        reader.onload = makeLoader(f);
+
+
+        // Text or data?
+        // This should likely be improved
+        if (f.type.indexOf('text') > -1) {
+          reader.readAsText(f);
+        } else {
+          reader.readAsDataURL(f);
+        }
+      }
+    }, this);
+  } else {
+    console.log('The File APIs are not fully supported in this browser.');
+  }
+
+  return this;
+};
+
+
+
+
+function attachListener(ev, fxn, ctx) {
+  // LM removing, not sure why we had this?
+  // var _this = ctx;
+  // var f = function (e) { fxn(e, _this); };
+  var f = fxn.bind(ctx);
+  ctx.elt.addEventListener(ev, f, false);
+  ctx._events[ev] = f;
+}
+
+/**
+ * Helper fxn for sharing pixel methods
+ *
+ */
+p5.Element.prototype._setProperty = function (prop, value) {
+  this[prop] = value;
+};
+
+
+module.exports = p5.Element;
+
+},{"./core":48}],53:[function(_dereq_,module,exports){
+/**
+ * @module Rendering
+ * @submodule Rendering
+ * @for p5
+ */
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+/**
+ * Thin wrapper around a renderer, to be used for creating a
+ * graphics buffer object. Use this class if you need
+ * to draw into an off-screen graphics buffer. The two parameters define the
+ * width and height in pixels. The fields and methods for this class are
+ * extensive, but mirror the normal drawing API for p5.
+ *
+ * @class p5.Graphics
+ * @constructor
+ * @extends p5.Element
+ * @param {String} elt DOM node that is wrapped
+ * @param {Object} [pInst] pointer to p5 instance
+ * @param {Boolean} whether we're using it as main canvas
+ */
+p5.Graphics = function(w, h, renderer, pInst) {
+
+  var r = renderer || constants.P2D;
+
+  var c = document.createElement('canvas');
+  var node = this._userNode || document.body;
+  node.appendChild(c);
+
+  p5.Element.call(this, c, pInst, false);
+  this._styles = [];
+  this.width = w;
+  this.height = h;
+  this._pixelDensity = pInst._pixelDensity;
+
+  if (r === constants.WEBGL) {
+    this._renderer = new p5.Renderer3D(c, pInst, false);
+  } else {
+    this._renderer = new p5.Renderer2D(c, pInst, false);
+  }
+
+  this._renderer.resize(w, h);
+  this._renderer._applyDefaults();
+
+  pInst._elements.push(this);
+
+  // bind methods and props of p5 to the new object
+  for (var p in p5.prototype) {
+    if (!this[p]) {
+      if (typeof p5.prototype[p] === 'function') {
+        this[p] = p5.prototype[p].bind(this);
+      } else {
+        this[p] = p5.prototype[p];
+      }
+    }
+  }
+
+  return this;
+};
+
+p5.Graphics.prototype = Object.create(p5.Element.prototype);
+
+module.exports = p5.Graphics;
+
+},{"./constants":47,"./core":48}],54:[function(_dereq_,module,exports){
+/**
+ * @module Rendering
+ * @submodule Rendering
+ * @for p5
+ */
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('../core/constants');
+
+/**
+ * Main graphics and rendering context, as well as the base API
+ * implementation for p5.js "core". To be used as the superclass for
+ * Renderer2D and Renderer3D classes, respecitvely.
+ *
+ * @class p5.Renderer
+ * @constructor
+ * @extends p5.Element
+ * @param {String} elt DOM node that is wrapped
+ * @param {Object} [pInst] pointer to p5 instance
+ * @param {Boolean} whether we're using it as main canvas
+ */
+p5.Renderer = function(elt, pInst, isMainCanvas) {
+  p5.Element.call(this, elt, pInst);
+  this.canvas = elt;
+  this._pInst = pInst;
+  if (isMainCanvas) {
+    this._isMainCanvas = true;
+    // for pixel method sharing with pimage
+    this._pInst._setProperty('_curElement', this);
+    this._pInst._setProperty('canvas', this.canvas);
+    this._pInst._setProperty('width', this.width);
+    this._pInst._setProperty('height', this.height);
+  } else { // hide if offscreen buffer by default
+    this.canvas.style.display = 'none';
+    this._styles = []; // non-main elt styles stored in p5.Renderer
+  }
+
+
+  this._textSize = 12;
+  this._textLeading = 15;
+  this._textFont = 'sans-serif';
+  this._textStyle = constants.NORMAL;
+  this._textAscent = null;
+  this._textDescent = null;
+
+
+  this._rectMode = constants.CORNER;
+  this._ellipseMode = constants.CENTER;
+  this._curveTightness = 0;
+  this._imageMode = constants.CORNER;
+
+  this._tint = null;
+  this._doStroke = true;
+  this._doFill = true;
+  this._strokeSet = false;
+  this._fillSet = false;
+  this._colorMode = constants.RGB;
+  this._colorMaxes = {
+    rgb: [255, 255, 255, 255],
+    hsb: [360, 100, 100, 1],
+    hsl: [360, 100, 100, 1]
+  };
+
+};
+
+p5.Renderer.prototype = Object.create(p5.Element.prototype);
+
+
+
+
+/**
+ * Resize our canvas element.
+ */
+p5.Renderer.prototype.resize = function(w, h) {
+  this.width = w;
+  this.height = h;
+  this.elt.width = w * this._pInst._pixelDensity;
+  this.elt.height = h * this._pInst._pixelDensity;
+  this.elt.style.width = w +'px';
+  this.elt.style.height = h + 'px';
+  if (this._isMainCanvas) {
+    this._pInst._setProperty('width', this.width);
+    this._pInst._setProperty('height', this.height);
+  }
+};
+
+p5.Renderer.prototype.textLeading = function(l) {
+
+  if (arguments.length && arguments[0]) {
+
+    this._setProperty('_textLeading', l);
+    return this;
+  }
+
+  return this._textLeading;
+};
+
+p5.Renderer.prototype.textSize = function(s) {
+
+  if (arguments.length && arguments[0]) {
+
+    this._setProperty('_textSize', s);
+    this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT);
+    return this._applyTextProperties();
+  }
+
+  return this._textSize;
+};
+
+p5.Renderer.prototype.textStyle = function(s) {
+
+  if (arguments.length && arguments[0]) {
+
+    if (s === constants.NORMAL ||
+      s === constants.ITALIC ||
+      s === constants.BOLD) {
+      this._setProperty('_textStyle', s);
+    }
+
+    return this._applyTextProperties();
+  }
+
+  return this._textStyle;
+};
+
+p5.Renderer.prototype.textAscent = function() {
+  if (this._textAscent === null) {
+    this._updateTextMetrics();
+  }
+  return this._textAscent;
+};
+
+p5.Renderer.prototype.textDescent = function() {
+
+  if (this._textDescent === null) {
+    this._updateTextMetrics();
+  }
+  return this._textDescent;
+};
+
+/**
+ * Helper fxn to check font type (system or otf)
+ */
+p5.Renderer.prototype._isOpenType = function(f) {
+
+  f = f || this._textFont;
+  return (typeof f === 'object' && f.font && f.font.supported);
+};
+
+p5.Renderer.prototype._updateTextMetrics = function() {
+
+  if (this._isOpenType()) {
+
+    this._setProperty('_textAscent', this._textFont._textAscent());
+    this._setProperty('_textDescent', this._textFont._textDescent());
+    return this;
+  }
+
+  // Adapted from http://stackoverflow.com/a/25355178
+  var text = document.createElement('span');
+  text.style.fontFamily = this._textFont;
+  text.style.fontSize = this._textSize + 'px';
+  text.innerHTML = 'ABCjgq|';
+
+  var block = document.createElement('div');
+  block.style.display = 'inline-block';
+  block.style.width = '1px';
+  block.style.height = '0px';
+
+  var container = document.createElement('div');
+  container.appendChild(text);
+  container.appendChild(block);
+
+  container.style.height = '0px';
+  container.style.overflow = 'hidden';
+  document.body.appendChild(container);
+
+  block.style.verticalAlign = 'baseline';
+  var blockOffset = calculateOffset(block);
+  var textOffset = calculateOffset(text);
+  var ascent = blockOffset[1] - textOffset[1];
+
+  block.style.verticalAlign = 'bottom';
+  blockOffset = calculateOffset(block);
+  textOffset = calculateOffset(text);
+  var height = blockOffset[1] - textOffset[1];
+  var descent = height - ascent;
+
+  document.body.removeChild(container);
+
+  this._setProperty('_textAscent', ascent);
+  this._setProperty('_textDescent', descent);
+
+  return this;
+};
+
+/**
+ * Helper fxn to measure ascent and descent.
+ * Adapted from http://stackoverflow.com/a/25355178
+ */
+function calculateOffset(object) {
+  var currentLeft = 0,
+    currentTop = 0;
+  if (object.offsetParent) {
+    do {
+      currentLeft += object.offsetLeft;
+      currentTop += object.offsetTop;
+    } while (object = object.offsetParent);
+  } else {
+    currentLeft += object.offsetLeft;
+    currentTop += object.offsetTop;
+  }
+  return [currentLeft, currentTop];
+}
+
+module.exports = p5.Renderer;
+
+},{"../core/constants":47,"./core":48}],55:[function(_dereq_,module,exports){
+
+var p5 = _dereq_('./core');
+var canvas = _dereq_('./canvas');
+var constants = _dereq_('./constants');
+var filters = _dereq_('../image/filters');
+
+_dereq_('./p5.Renderer');
+
+/**
+ * p5.Renderer2D
+ * The 2D graphics canvas renderer class.
+ * extends p5.Renderer
+ */
+var styleEmpty = 'rgba(0,0,0,0)';
+// var alphaThreshold = 0.00125; // minimum visible
+
+p5.Renderer2D = function(elt, pInst, isMainCanvas){
+  p5.Renderer.call(this, elt, pInst, isMainCanvas);
+  this.drawingContext = this.canvas.getContext('2d');
+  this._pInst._setProperty('drawingContext', this.drawingContext);
+  return this;
+};
+
+p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype);
+
+p5.Renderer2D.prototype._applyDefaults = function() {
+  this.drawingContext.fillStyle = constants._DEFAULT_FILL;
+  this.drawingContext.strokeStyle = constants._DEFAULT_STROKE;
+  this.drawingContext.lineCap = constants.ROUND;
+  this.drawingContext.font = 'normal 12px sans-serif';
+};
+
+p5.Renderer2D.prototype.resize = function(w,h) {
+  p5.Renderer.prototype.resize.call(this, w,h);
+  this.drawingContext.scale(this._pInst._pixelDensity,
+                            this._pInst._pixelDensity);
+};
+
+//////////////////////////////////////////////
+// COLOR | Setting
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.background = function() {
+  this.drawingContext.save();
+  this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
+  this.drawingContext.scale(this._pInst._pixelDensity,
+                            this._pInst._pixelDensity);
+
+  if (arguments[0] instanceof p5.Image) {
+    this._pInst.image(arguments[0], 0, 0, this.width, this.height);
+  } else {
+    var curFill = this.drawingContext.fillStyle;
+    // create background rect
+    var color = this._pInst.color.apply(this, arguments);
+    var newFill = color.toString();
+    this.drawingContext.fillStyle = newFill;
+    this.drawingContext.fillRect(0, 0, this.width, this.height);
+    // reset fill
+    this.drawingContext.fillStyle = curFill;
+  }
+  this.drawingContext.restore();
+};
+
+p5.Renderer2D.prototype.clear = function() {
+  this.drawingContext.clearRect(0, 0, this.width, this.height);
+};
+
+p5.Renderer2D.prototype.fill = function() {
+
+  var ctx = this.drawingContext;
+  var color = this._pInst.color.apply(this, arguments);
+  ctx.fillStyle = color.toString();
+};
+
+p5.Renderer2D.prototype.stroke = function() {
+  var ctx = this.drawingContext;
+  var color = this._pInst.color.apply(this, arguments);
+  ctx.strokeStyle = color.toString();
+};
+
+//////////////////////////////////////////////
+// IMAGE | Loading & Displaying
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.image =
+  function (img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) {
+  var cnv;
+  try {
+    if (this._tint) {
+      if (p5.MediaElement && img instanceof p5.MediaElement) {
+        img.loadPixels();
+      }
+      if (img.canvas) {
+        cnv = this._getTintedImageCanvas(img);
+      }
+    }
+    if (!cnv) {
+      cnv = img.canvas || img.elt;
+    }
+    this.drawingContext.drawImage(cnv, sx, sy, sWidth, sHeight, dx, dy,
+      dWidth, dHeight);
+  } catch (e) {
+    if (e.name !== 'NS_ERROR_NOT_AVAILABLE') {
+      throw e;
+    }
+  }
+};
+
+p5.Renderer2D.prototype._getTintedImageCanvas = function (img) {
+  if (!img.canvas) {
+    return img;
+  }
+  var pixels = filters._toPixels(img.canvas);
+  var tmpCanvas = document.createElement('canvas');
+  tmpCanvas.width = img.canvas.width;
+  tmpCanvas.height = img.canvas.height;
+  var tmpCtx = tmpCanvas.getContext('2d');
+  var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height);
+  var newPixels = id.data;
+  for (var i = 0; i < pixels.length; i += 4) {
+    var r = pixels[i];
+    var g = pixels[i + 1];
+    var b = pixels[i + 2];
+    var a = pixels[i + 3];
+    newPixels[i] = r * this._tint[0] / 255;
+    newPixels[i + 1] = g * this._tint[1] / 255;
+    newPixels[i + 2] = b * this._tint[2] / 255;
+    newPixels[i + 3] = a * this._tint[3] / 255;
+  }
+  tmpCtx.putImageData(id, 0, 0);
+  return tmpCanvas;
+};
+
+
+//////////////////////////////////////////////
+// IMAGE | Pixels
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.blendMode = function(mode) {
+  this.drawingContext.globalCompositeOperation = mode;
+};
+p5.Renderer2D.prototype.blend = function() {
+  var currBlend = this.drawingContext.globalCompositeOperation;
+  var blendMode = arguments[arguments.length - 1];
+
+  var copyArgs = Array.prototype.slice.call(
+    arguments,
+    0,
+    arguments.length - 1
+  );
+
+  this.drawingContext.globalCompositeOperation = blendMode;
+  this._pInst.copy.apply(this._pInst, copyArgs);
+  this.drawingContext.globalCompositeOperation = currBlend;
+};
+
+p5.Renderer2D.prototype.copy = function () {
+  var srcImage, sx, sy, sw, sh, dx, dy, dw, dh;
+  if (arguments.length === 9) {
+    srcImage = arguments[0];
+    sx = arguments[1];
+    sy = arguments[2];
+    sw = arguments[3];
+    sh = arguments[4];
+    dx = arguments[5];
+    dy = arguments[6];
+    dw = arguments[7];
+    dh = arguments[8];
+  } else if (arguments.length === 8) {
+    srcImage = this._pInst;
+    sx = arguments[0];
+    sy = arguments[1];
+    sw = arguments[2];
+    sh = arguments[3];
+    dx = arguments[4];
+    dy = arguments[5];
+    dw = arguments[6];
+    dh = arguments[7];
+  } else {
+    throw new Error('Signature not supported');
+  }
+  p5.Renderer2D._copyHelper(srcImage, sx, sy, sw, sh, dx, dy, dw, dh);
+};
+
+p5.Renderer2D._copyHelper =
+function (srcImage, sx, sy, sw, sh, dx, dy, dw, dh) {
+  var s = srcImage.canvas.width / srcImage.width;
+  this.drawingContext.drawImage(srcImage.canvas,
+    s * sx, s * sy, s * sw, s * sh, dx, dy, dw, dh);
+};
+
+p5.Renderer2D.prototype.get = function(x, y, w, h) {
+  if (x === undefined && y === undefined &&
+      w === undefined && h === undefined){
+    x = 0;
+    y = 0;
+    w = this.width;
+    h = this.height;
+  } else if (w === undefined && h === undefined) {
+    w = 1;
+    h = 1;
+  }
+
+  // if the section does not overlap the canvas
+  if(x + w < 0 || y + h < 0 || x > this.width || y > this.height){
+    return [0, 0, 0, 255];
+  }
+
+  var ctx = this._pInst || this;
+
+  var pd = ctx._pixelDensity;
+
+  this.loadPixels.call(ctx);
+
+  // round down to get integer numbers
+  x = Math.floor(x);
+  y = Math.floor(y);
+
+  if (w === 1 && h === 1){
+
+    return [
+      ctx.pixels[pd*4*(y*this.width+x)],
+      ctx.pixels[pd*(4*(y*this.width+x)+1)],
+      ctx.pixels[pd*(4*(y*this.width+x)+2)],
+      ctx.pixels[pd*(4*(y*this.width+x)+3)]
+    ];
+  } else {
+    var sx = x * pd;
+    var sy = y * pd;
+    //auto constrain the width and height to
+    //dimensions of the source image
+    var dw = Math.min(w, ctx.width);
+    var dh = Math.min(h, ctx.height);
+    var sw = dw * pd;
+    var sh = dh * pd;
+
+    var region = new p5.Image(dw, dh);
+    region.canvas.getContext('2d').drawImage(this.canvas, sx, sy, sw, sh,
+      0, 0, dw, dh);
+
+    return region;
+  }
+};
+
+p5.Renderer2D.prototype.loadPixels = function () {
+  var pd = this._pixelDensity || this._pInst._pixelDensity;
+  var w = this.width * pd;
+  var h = this.height * pd;
+  var imageData = this.drawingContext.getImageData(0, 0, w, h);
+  // @todo this should actually set pixels per object, so diff buffers can
+  // have diff pixel arrays.
+  if (this._pInst) {
+    this._pInst._setProperty('imageData', imageData);
+    this._pInst._setProperty('pixels', imageData.data);
+  } else { // if called by p5.Image
+    this._setProperty('imageData', imageData);
+    this._setProperty('pixels', imageData.data);
+  }
+};
+
+p5.Renderer2D.prototype.set = function (x, y, imgOrCol) {
+  // round down to get integer numbers
+  x = Math.floor(x);
+  y = Math.floor(y);
+  if (imgOrCol instanceof p5.Image) {
+    this.drawingContext.save();
+    this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
+    this.drawingContext.scale(this._pInst._pixelDensity,
+      this._pInst._pixelDensity);
+    this.drawingContext.drawImage(imgOrCol.canvas, x, y);
+    this.loadPixels.call(this._pInst);
+    this.drawingContext.restore();
+  } else {
+    var ctx = this._pInst || this;
+    var r = 0, g = 0, b = 0, a = 0;
+    var idx = 4*((y * ctx._pixelDensity) *
+      (this.width * ctx._pixelDensity) + (x * ctx._pixelDensity));
+    if (!ctx.imageData) {
+      ctx.loadPixels.call(ctx);
+    }
+    if (typeof imgOrCol === 'number') {
+      if (idx < ctx.pixels.length) {
+        r = imgOrCol;
+        g = imgOrCol;
+        b = imgOrCol;
+        a = 255;
+        //this.updatePixels.call(this);
+      }
+    }
+    else if (imgOrCol instanceof Array) {
+      if (imgOrCol.length < 4) {
+        throw new Error('pixel array must be of the form [R, G, B, A]');
+      }
+      if (idx < ctx.pixels.length) {
+        r = imgOrCol[0];
+        g = imgOrCol[1];
+        b = imgOrCol[2];
+        a = imgOrCol[3];
+        //this.updatePixels.call(this);
+      }
+    } else if (imgOrCol instanceof p5.Color) {
+      if (idx < ctx.pixels.length) {
+        r = imgOrCol.levels[0];
+        g = imgOrCol.levels[1];
+        b = imgOrCol.levels[2];
+        a = imgOrCol.levels[3];
+        //this.updatePixels.call(this);
+      }
+    }
+    // loop over pixelDensity * pixelDensity
+    for (var i = 0; i < ctx._pixelDensity; i++) {
+      for (var j = 0; j < ctx._pixelDensity; j++) {
+        // loop over
+        idx = 4*((y * ctx._pixelDensity + j) * this.width *
+          ctx._pixelDensity + (x * ctx._pixelDensity + i));
+        ctx.pixels[idx] = r;
+        ctx.pixels[idx+1] = g;
+        ctx.pixels[idx+2] = b;
+        ctx.pixels[idx+3] = a;
+      }
+    }
+  }
+};
+
+p5.Renderer2D.prototype.updatePixels = function (x, y, w, h) {
+  var pd = this._pixelDensity || this._pInst._pixelDensity;
+  if (x === undefined &&
+      y === undefined &&
+      w === undefined &&
+      h === undefined) {
+    x = 0;
+    y = 0;
+    w = this.width;
+    h = this.height;
+  }
+  w *= pd;
+  h *= pd;
+
+  if (this._pInst) {
+    this.drawingContext.putImageData(this._pInst.imageData, x, y, 0, 0, w, h);
+  } else {
+    this.drawingContext.putImageData(this.imageData, x, y, 0, 0, w, h);
+  }
+};
+
+//////////////////////////////////////////////
+// SHAPE | 2D Primitives
+//////////////////////////////////////////////
+
+/**
+ * Generate a cubic Bezier representing an arc on the unit circle of total
+ * angle `size` radians, beginning `start` radians above the x-axis. Up to
+ * four of these curves are combined to make a full arc.
+ *
+ * See www.joecridge.me/bezier.pdf for an explanation of the method.
+ */
+p5.Renderer2D.prototype._acuteArcToBezier =
+  function _acuteArcToBezier(start, size) {
+  // Evauate constants.
+  var alpha = size / 2.0,
+    cos_alpha = Math.cos(alpha),
+    sin_alpha = Math.sin(alpha),
+    cot_alpha = 1.0 / Math.tan(alpha),
+    phi = start + alpha,  // This is how far the arc needs to be rotated.
+    cos_phi = Math.cos(phi),
+    sin_phi = Math.sin(phi),
+    lambda = (4.0 - cos_alpha) / 3.0,
+    mu = sin_alpha + (cos_alpha - lambda) * cot_alpha;
+
+  // Return rotated waypoints.
+  return {
+    ax: Math.cos(start),
+    ay: Math.sin(start),
+    bx: lambda * cos_phi + mu * sin_phi,
+    by: lambda * sin_phi - mu * cos_phi,
+    cx: lambda * cos_phi - mu * sin_phi,
+    cy: lambda * sin_phi + mu * cos_phi,
+    dx: Math.cos(start + size),
+    dy: Math.sin(start + size)
+  };
+};
+
+p5.Renderer2D.prototype.arc =
+  function(x, y, w, h, start, stop, mode) {
+  var ctx = this.drawingContext;
+  var vals = canvas.arcModeAdjust(x, y, w, h, this._ellipseMode);
+  var rx = vals.w / 2.0;
+  var ry = vals.h / 2.0;
+  var epsilon = 0.00001;  // Smallest visible angle on displays up to 4K.
+  var arcToDraw = 0;
+  var curves = [];
+
+  // Create curves
+  while(stop - start > epsilon) {
+    arcToDraw = Math.min(stop - start, constants.HALF_PI);
+    curves.push(this._acuteArcToBezier(start, arcToDraw));
+    start += arcToDraw;
+  }
+
+  // Fill curves
+  if (this._doFill) {
+    ctx.beginPath();
+    curves.forEach(function (curve, index) {
+      if (index === 0) {
+        ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry);
+      }
+      ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry,
+                        vals.x + curve.cx * rx, vals.y + curve.cy * ry,
+                        vals.x + curve.dx * rx, vals.y + curve.dy * ry);
+    });
+    if (mode === constants.PIE || mode == null) {
+      ctx.lineTo(vals.x, vals.y);
+    }
+    ctx.closePath();
+    ctx.fill();
+  }
+
+  // Stroke curves
+  if (this._doStroke) {
+    ctx.beginPath();
+    curves.forEach(function (curve, index) {
+      if (index === 0) {
+        ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry);
+      }
+      ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry,
+                        vals.x + curve.cx * rx, vals.y + curve.cy * ry,
+                        vals.x + curve.dx * rx, vals.y + curve.dy * ry);
+    });
+    if (mode === constants.PIE) {
+      ctx.lineTo(vals.x, vals.y);
+      ctx.closePath();
+    } else if (mode === constants.CHORD) {
+      ctx.closePath();
+    }
+    ctx.stroke();
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.ellipse = function(x, y, w, h) {
+  var ctx = this.drawingContext;
+  var doFill = this._doFill, doStroke = this._doStroke;
+  if (doFill && !doStroke) {
+    if(ctx.fillStyle === styleEmpty) {
+      return this;
+    }
+  } else if (!doFill && doStroke) {
+    if(ctx.strokeStyle === styleEmpty) {
+      return this;
+    }
+  }
+  var vals = canvas.modeAdjust(x, y, w, h, this._ellipseMode);
+  var kappa = 0.5522847498,
+    ox = (vals.w / 2) * kappa, // control point offset horizontal
+    oy = (vals.h / 2) * kappa, // control point offset vertical
+    xe = vals.x + vals.w,      // x-end
+    ye = vals.y + vals.h,      // y-end
+    xm = vals.x + vals.w / 2,  // x-middle
+    ym = vals.y + vals.h / 2;  // y-middle
+  ctx.beginPath();
+  ctx.moveTo(vals.x, ym);
+  ctx.bezierCurveTo(vals.x, ym - oy, xm - ox, vals.y, xm, vals.y);
+  ctx.bezierCurveTo(xm + ox, vals.y, xe, ym - oy, xe, ym);
+  ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+  ctx.bezierCurveTo(xm - ox, ye, vals.x, ym + oy, vals.x, ym);
+  ctx.closePath();
+  if (doFill) {
+    ctx.fill();
+  }
+  if (doStroke) {
+    ctx.stroke();
+  }
+};
+
+p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) {
+  var ctx = this.drawingContext;
+  if (!this._doStroke) {
+    return this;
+  } else if(ctx.strokeStyle === styleEmpty){
+    return this;
+  }
+  // Translate the line by (0.5, 0.5) to draw it crisp
+  if (ctx.lineWidth % 2 === 1) {
+    ctx.translate(0.5, 0.5);
+  }
+  ctx.beginPath();
+  ctx.moveTo(x1, y1);
+  ctx.lineTo(x2, y2);
+  ctx.stroke();
+  if (ctx.lineWidth % 2 === 1) {
+    ctx.translate(-0.5, -0.5);
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.point = function(x, y) {
+  var ctx = this.drawingContext;
+  var s = ctx.strokeStyle;
+  var f = ctx.fillStyle;
+  if (!this._doStroke) {
+    return this;
+  } else if(ctx.strokeStyle === styleEmpty){
+    return this;
+  }
+  x = Math.round(x);
+  y = Math.round(y);
+  ctx.fillStyle = s;
+  if (ctx.lineWidth > 1) {
+    ctx.beginPath();
+    ctx.arc(
+      x,
+      y,
+      ctx.lineWidth / 2,
+      0,
+      constants.TWO_PI,
+      false
+    );
+    ctx.fill();
+  } else {
+    ctx.fillRect(x, y, 1, 1);
+  }
+  ctx.fillStyle = f;
+};
+
+p5.Renderer2D.prototype.quad =
+  function(x1, y1, x2, y2, x3, y3, x4, y4) {
+  var ctx = this.drawingContext;
+  var doFill = this._doFill, doStroke = this._doStroke;
+  if (doFill && !doStroke) {
+    if(ctx.fillStyle === styleEmpty) {
+      return this;
+    }
+  } else if (!doFill && doStroke) {
+    if(ctx.strokeStyle === styleEmpty) {
+      return this;
+    }
+  }
+  ctx.beginPath();
+  ctx.moveTo(x1, y1);
+  ctx.lineTo(x2, y2);
+  ctx.lineTo(x3, y3);
+  ctx.lineTo(x4, y4);
+  ctx.closePath();
+  if (doFill) {
+    ctx.fill();
+  }
+  if (doStroke) {
+    ctx.stroke();
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.rect = function(x, y, w, h, tl, tr, br, bl) {
+  var ctx = this.drawingContext;
+  var doFill = this._doFill, doStroke = this._doStroke;
+  if (doFill && !doStroke) {
+    if(ctx.fillStyle === styleEmpty) {
+      return this;
+    }
+  } else if (!doFill && doStroke) {
+    if(ctx.strokeStyle === styleEmpty) {
+      return this;
+    }
+  }
+  var vals = canvas.modeAdjust(x, y, w, h, this._rectMode);
+  // Translate the line by (0.5, 0.5) to draw a crisp rectangle border
+  if (this._doStroke && ctx.lineWidth % 2 === 1) {
+    ctx.translate(0.5, 0.5);
+  }
+  ctx.beginPath();
+
+  if (typeof tl === 'undefined') {
+    // No rounded corners
+    ctx.rect(vals.x, vals.y, vals.w, vals.h);
+  } else {
+    // At least one rounded corner
+    // Set defaults when not specified
+    if (typeof tr === 'undefined') { tr = tl; }
+    if (typeof br === 'undefined') { br = tr; }
+    if (typeof bl === 'undefined') { bl = br; }
+
+    // Cache and compute several values
+    var _x = vals.x;
+    var _y = vals.y;
+    var _w = vals.w;
+    var _h = vals.h;
+    var hw = _w / 2;
+    var hh = _h / 2;
+
+    // Clip radii
+    if (_w < 2 * tl) { tl = hw; }
+    if (_h < 2 * tl) { tl = hh; }
+    if (_w < 2 * tr) { tr = hw; }
+    if (_h < 2 * tr) { tr = hh; }
+    if (_w < 2 * br) { br = hw; }
+    if (_h < 2 * br) { br = hh; }
+    if (_w < 2 * bl) { bl = hw; }
+    if (_h < 2 * bl) { bl = hh; }
+
+    // Draw shape
+    ctx.beginPath();
+    ctx.moveTo(_x + tl, _y);
+    ctx.arcTo(_x + _w, _y, _x + _w, _y + _h, tr);
+    ctx.arcTo(_x + _w, _y + _h, _x, _y + _h, br);
+    ctx.arcTo(_x, _y + _h, _x, _y, bl);
+    ctx.arcTo(_x, _y, _x + _w, _y, tl);
+    ctx.closePath();
+  }
+  if (this._doFill) {
+    ctx.fill();
+  }
+  if (this._doStroke) {
+    ctx.stroke();
+  }
+  if (this._doStroke && ctx.lineWidth % 2 === 1) {
+    ctx.translate(-0.5, -0.5);
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.triangle = function(x1, y1, x2, y2, x3, y3) {
+  var ctx = this.drawingContext;
+  var doFill = this._doFill, doStroke = this._doStroke;
+  if (doFill && !doStroke) {
+    if(ctx.fillStyle === styleEmpty) {
+      return this;
+    }
+  } else if (!doFill && doStroke) {
+    if(ctx.strokeStyle === styleEmpty) {
+      return this;
+    }
+  }
+  ctx.beginPath();
+  ctx.moveTo(x1, y1);
+  ctx.lineTo(x2, y2);
+  ctx.lineTo(x3, y3);
+  ctx.closePath();
+  if (doFill) {
+    ctx.fill();
+  }
+  if (doStroke) {
+    ctx.stroke();
+  }
+};
+
+p5.Renderer2D.prototype.endShape =
+function (mode, vertices, isCurve, isBezier,
+    isQuadratic, isContour, shapeKind) {
+  if (vertices.length === 0) {
+    return this;
+  }
+  if (!this._doStroke && !this._doFill) {
+    return this;
+  }
+  var closeShape = mode === constants.CLOSE;
+  var v;
+  if (closeShape && !isContour) {
+    vertices.push(vertices[0]);
+  }
+  var i, j;
+  var numVerts = vertices.length;
+  if (isCurve && (shapeKind === constants.POLYGON || shapeKind === null)) {
+    if (numVerts > 3) {
+      var b = [], s = 1 - this._curveTightness;
+      this.drawingContext.beginPath();
+      this.drawingContext.moveTo(vertices[1][0], vertices[1][1]);
+      for (i = 1; i + 2 < numVerts; i++) {
+        v = vertices[i];
+        b[0] = [
+          v[0],
+          v[1]
+        ];
+        b[1] = [
+          v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6,
+          v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6
+        ];
+        b[2] = [
+          vertices[i + 1][0] +
+          (s * vertices[i][0]-s * vertices[i + 2][0]) / 6,
+          vertices[i + 1][1]+(s * vertices[i][1] - s*vertices[i + 2][1]) / 6
+        ];
+        b[3] = [
+          vertices[i + 1][0],
+          vertices[i + 1][1]
+        ];
+        this.drawingContext.bezierCurveTo(b[1][0],b[1][1],
+          b[2][0],b[2][1],b[3][0],b[3][1]);
+      }
+      if (closeShape) {
+        this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]);
+      }
+      this._doFillStrokeClose();
+    }
+  } else if (isBezier&&(shapeKind===constants.POLYGON ||shapeKind === null)) {
+    this.drawingContext.beginPath();
+    for (i = 0; i < numVerts; i++) {
+      if (vertices[i].isVert) {
+        if (vertices[i].moveTo) {
+          this.drawingContext.moveTo(vertices[i][0], vertices[i][1]);
+        } else {
+          this.drawingContext.lineTo(vertices[i][0], vertices[i][1]);
+        }
+      } else {
+        this.drawingContext.bezierCurveTo(vertices[i][0], vertices[i][1],
+          vertices[i][2], vertices[i][3], vertices[i][4], vertices[i][5]);
+      }
+    }
+    this._doFillStrokeClose();
+  } else if (isQuadratic &&
+    (shapeKind === constants.POLYGON || shapeKind === null)) {
+    this.drawingContext.beginPath();
+    for (i = 0; i < numVerts; i++) {
+      if (vertices[i].isVert) {
+        if (vertices[i].moveTo) {
+          this.drawingContext.moveTo([0], vertices[i][1]);
+        } else {
+          this.drawingContext.lineTo(vertices[i][0], vertices[i][1]);
+        }
+      } else {
+        this.drawingContext.quadraticCurveTo(vertices[i][0], vertices[i][1],
+          vertices[i][2], vertices[i][3]);
+      }
+    }
+    this._doFillStrokeClose();
+  } else {
+    if (shapeKind === constants.POINTS) {
+      for (i = 0; i < numVerts; i++) {
+        v = vertices[i];
+        if (this._doStroke) {
+          this._pInst.stroke(v[6]);
+        }
+        this._pInst.point(v[0], v[1]);
+      }
+    } else if (shapeKind === constants.LINES) {
+      for (i = 0; i + 1 < numVerts; i += 2) {
+        v = vertices[i];
+        if (this._doStroke) {
+          this._pInst.stroke(vertices[i + 1][6]);
+        }
+        this._pInst.line(v[0], v[1], vertices[i + 1][0], vertices[i + 1][1]);
+      }
+    } else if (shapeKind === constants.TRIANGLES) {
+      for (i = 0; i + 2 < numVerts; i += 3) {
+        v = vertices[i];
+        this.drawingContext.beginPath();
+        this.drawingContext.moveTo(v[0], v[1]);
+        this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]);
+        this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]);
+        this.drawingContext.lineTo(v[0], v[1]);
+        if (this._doFill) {
+          this._pInst.fill(vertices[i + 2][5]);
+          this.drawingContext.fill();
+        }
+        if (this._doStroke) {
+          this._pInst.stroke(vertices[i + 2][6]);
+          this.drawingContext.stroke();
+        }
+        this.drawingContext.closePath();
+      }
+    } else if (shapeKind === constants.TRIANGLE_STRIP) {
+      for (i = 0; i + 1 < numVerts; i++) {
+        v = vertices[i];
+        this.drawingContext.beginPath();
+        this.drawingContext.moveTo(vertices[i + 1][0], vertices[i + 1][1]);
+        this.drawingContext.lineTo(v[0], v[1]);
+        if (this._doStroke) {
+          this._pInst.stroke(vertices[i + 1][6]);
+        }
+        if (this._doFill) {
+          this._pInst.fill(vertices[i + 1][5]);
+        }
+        if (i + 2 < numVerts) {
+          this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]);
+          if (this._doStroke) {
+            this._pInst.stroke(vertices[i + 2][6]);
+          }
+          if (this._doFill) {
+            this._pInst.fill(vertices[i + 2][5]);
+          }
+        }
+        this._doFillStrokeClose();
+      }
+    } else if (shapeKind === constants.TRIANGLE_FAN) {
+      if (numVerts > 2) {
+        this.drawingContext.beginPath();
+        this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
+        this.drawingContext.lineTo(vertices[1][0], vertices[1][1]);
+        this.drawingContext.lineTo(vertices[2][0], vertices[2][1]);
+        if (this._doFill) {
+          this._pInst.fill(vertices[2][5]);
+        }
+        if (this._doStroke) {
+          this._pInst.stroke(vertices[2][6]);
+        }
+        this._doFillStrokeClose();
+        for (i = 3; i < numVerts; i++) {
+          v = vertices[i];
+          this.drawingContext.beginPath();
+          this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
+          this.drawingContext.lineTo(vertices[i - 1][0], vertices[i - 1][1]);
+          this.drawingContext.lineTo(v[0], v[1]);
+          if (this._doFill) {
+            this._pInst.fill(v[5]);
+          }
+          if (this._doStroke) {
+            this._pInst.stroke(v[6]);
+          }
+          this._doFillStrokeClose();
+        }
+      }
+    } else if (shapeKind === constants.QUADS) {
+      for (i = 0; i + 3 < numVerts; i += 4) {
+        v = vertices[i];
+        this.drawingContext.beginPath();
+        this.drawingContext.moveTo(v[0], v[1]);
+        for (j = 1; j < 4; j++) {
+          this.drawingContext.lineTo(vertices[i + j][0], vertices[i + j][1]);
+        }
+        this.drawingContext.lineTo(v[0], v[1]);
+        if (this._doFill) {
+          this._pInst.fill(vertices[i + 3][5]);
+        }
+        if (this._doStroke) {
+          this._pInst.stroke(vertices[i + 3][6]);
+        }
+        this._doFillStrokeClose();
+      }
+    } else if (shapeKind === constants.QUAD_STRIP) {
+      if (numVerts > 3) {
+        for (i = 0; i + 1 < numVerts; i += 2) {
+          v = vertices[i];
+          this.drawingContext.beginPath();
+          if (i + 3 < numVerts) {
+            this.drawingContext.moveTo(vertices[i + 2][0], vertices[i+2][1]);
+            this.drawingContext.lineTo(v[0], v[1]);
+            this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]);
+            this.drawingContext.lineTo(vertices[i + 3][0], vertices[i+3][1]);
+            if (this._doFill) {
+              this._pInst.fill(vertices[i + 3][5]);
+            }
+            if (this._doStroke) {
+              this._pInst.stroke(vertices[i + 3][6]);
+            }
+          } else {
+            this.drawingContext.moveTo(v[0], v[1]);
+            this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]);
+          }
+          this._doFillStrokeClose();
+        }
+      }
+    } else {
+      this.drawingContext.beginPath();
+      this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
+      for (i = 1; i < numVerts; i++) {
+        v = vertices[i];
+        if (v.isVert) {
+          if (v.moveTo) {
+            this.drawingContext.moveTo(v[0], v[1]);
+          } else {
+            this.drawingContext.lineTo(v[0], v[1]);
+          }
+        }
+      }
+      this._doFillStrokeClose();
+    }
+  }
+  isCurve = false;
+  isBezier = false;
+  isQuadratic = false;
+  isContour = false;
+  if (closeShape) {
+    vertices.pop();
+  }
+  return this;
+};
+//////////////////////////////////////////////
+// SHAPE | Attributes
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.noSmooth = function() {
+  if ('imageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.imageSmoothingEnabled = false;
+  }
+  else if ('mozImageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.mozImageSmoothingEnabled = false;
+  }
+  else if ('webkitImageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.webkitImageSmoothingEnabled = false;
+  }
+  else if ('msImageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.msImageSmoothingEnabled = false;
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.smooth = function() {
+  if ('imageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.imageSmoothingEnabled = true;
+  }
+  else if ('mozImageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.mozImageSmoothingEnabled = true;
+  }
+  else if ('webkitImageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.webkitImageSmoothingEnabled = true;
+  }
+  else if ('msImageSmoothingEnabled' in this.drawingContext) {
+    this.drawingContext.msImageSmoothingEnabled = true;
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.strokeCap = function(cap) {
+  if (cap === constants.ROUND ||
+    cap === constants.SQUARE ||
+    cap === constants.PROJECT) {
+    this.drawingContext.lineCap = cap;
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.strokeJoin = function(join) {
+  if (join === constants.ROUND ||
+    join === constants.BEVEL ||
+    join === constants.MITER) {
+    this.drawingContext.lineJoin = join;
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype.strokeWeight = function(w) {
+  if (typeof w === 'undefined' || w === 0) {
+    // hack because lineWidth 0 doesn't work
+    this.drawingContext.lineWidth = 0.0001;
+  } else {
+    this.drawingContext.lineWidth = w;
+  }
+  return this;
+};
+
+p5.Renderer2D.prototype._getFill = function(){
+  return this.drawingContext.fillStyle;
+};
+
+p5.Renderer2D.prototype._getStroke = function(){
+  return this.drawingContext.strokeStyle;
+};
+
+//////////////////////////////////////////////
+// SHAPE | Curves
+//////////////////////////////////////////////
+p5.Renderer2D.prototype.bezier = function (x1, y1, x2, y2, x3, y3, x4, y4) {
+  this._pInst.beginShape();
+  this._pInst.vertex(x1, y1);
+  this._pInst.bezierVertex(x2, y2, x3, y3, x4, y4);
+  this._pInst.endShape();
+  return this;
+};
+
+p5.Renderer2D.prototype.curve = function (x1, y1, x2, y2, x3, y3, x4, y4) {
+  this._pInst.beginShape();
+  this._pInst.curveVertex(x1, y1);
+  this._pInst.curveVertex(x2, y2);
+  this._pInst.curveVertex(x3, y3);
+  this._pInst.curveVertex(x4, y4);
+  this._pInst.endShape();
+  return this;
+};
+
+//////////////////////////////////////////////
+// SHAPE | Vertex
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype._doFillStrokeClose = function () {
+  if (this._doFill) {
+    this.drawingContext.fill();
+  }
+  if (this._doStroke) {
+    this.drawingContext.stroke();
+  }
+  this.drawingContext.closePath();
+};
+
+//////////////////////////////////////////////
+// TRANSFORM
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.applyMatrix =
+function(n00, n01, n02, n10, n11, n12) {
+  this.drawingContext.transform(n00, n01, n02, n10, n11, n12);
+};
+
+p5.Renderer2D.prototype.resetMatrix = function() {
+  this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
+  this.drawingContext.scale(this._pInst._pixelDensity,
+                            this._pInst._pixelDensity);
+  return this;
+};
+
+p5.Renderer2D.prototype.rotate = function(r) {
+  this.drawingContext.rotate(r);
+};
+
+p5.Renderer2D.prototype.scale = function(x,y) {
+  this.drawingContext.scale(x, y);
+  return this;
+};
+
+p5.Renderer2D.prototype.shearX = function(angle) {
+  if (this._pInst._angleMode === constants.DEGREES) {
+    angle = this._pInst.radians(angle);
+  }
+  this.drawingContext.transform(1, 0, this._pInst.tan(angle), 1, 0, 0);
+  return this;
+};
+
+p5.Renderer2D.prototype.shearY = function(angle) {
+  if (this._pInst._angleMode === constants.DEGREES) {
+    angle = this._pInst.radians(angle);
+  }
+  this.drawingContext.transform(1, this._pInst.tan(angle), 0, 1, 0, 0);
+  return this;
+};
+
+p5.Renderer2D.prototype.translate = function(x, y) {
+  this.drawingContext.translate(x, y);
+  return this;
+};
+
+//////////////////////////////////////////////
+// TYPOGRAPHY
+//
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.text = function (str, x, y, maxWidth, maxHeight) {
+
+  var p = this._pInst, cars, n, ii, jj, line, testLine,
+    testWidth, words, totalHeight, baselineHacked,
+    finalMaxHeight = Number.MAX_VALUE;
+
+  // baselineHacked: (HACK)
+  // A temporary fix to conform to Processing's implementation
+  // of BASELINE vertical alignment in a bounding box
+
+  if (!(this._doFill || this._doStroke)) {
+    return;
+  }
+
+  if (typeof str !== 'string') {
+    str = str.toString();
+  }
+
+  str = str.replace(/(\t)/g, '  ');
+  cars = str.split('\n');
+
+  if (typeof maxWidth !== 'undefined') {
+
+    totalHeight = 0;
+    for (ii = 0; ii < cars.length; ii++) {
+      line = '';
+      words = cars[ii].split(' ');
+      for (n = 0; n < words.length; n++) {
+        testLine = line + words[n] + ' ';
+        testWidth = this.textWidth(testLine);
+        if (testWidth > maxWidth) {
+          line = words[n] + ' ';
+          totalHeight += p.textLeading();
+        } else {
+          line = testLine;
+        }
+      }
+    }
+
+    if (this._rectMode === constants.CENTER) {
+
+      x -= maxWidth / 2;
+      y -= maxHeight / 2;
+    }
+
+    switch (this.drawingContext.textAlign) {
+
+    case constants.CENTER:
+      x += maxWidth / 2;
+      break;
+    case constants.RIGHT:
+      x += maxWidth;
+      break;
+    }
+
+    if (typeof maxHeight !== 'undefined') {
+
+      switch (this.drawingContext.textBaseline) {
+      case constants.BOTTOM:
+        y += (maxHeight - totalHeight);
+        break;
+      case constants._CTX_MIDDLE:
+        y += (maxHeight - totalHeight) / 2;
+        break;
+      case constants.BASELINE:
+        baselineHacked = true;
+        this.drawingContext.textBaseline = constants.TOP;
+        break;
+      }
+
+      // remember the max-allowed y-position for any line (fix to #928)
+      finalMaxHeight = (y + maxHeight) - p.textAscent();
+    }
+
+    for (ii = 0; ii < cars.length; ii++) {
+
+      line = '';
+      words = cars[ii].split(' ');
+      for (n = 0; n < words.length; n++) {
+        testLine = line + words[n] + ' ';
+        testWidth = this.textWidth(testLine);
+        if (testWidth > maxWidth && line.length > 0) {
+          this._renderText(p, line, x, y, finalMaxHeight);
+          line = words[n] + ' ';
+          y += p.textLeading();
+        } else {
+          line = testLine;
+        }
+      }
+
+      this._renderText(p, line, x, y, finalMaxHeight);
+      y += p.textLeading();
+    }
+  }
+  else {
+    for (jj = 0; jj < cars.length; jj++) {
+
+      this._renderText(p, cars[jj], x, y, finalMaxHeight);
+      y += p.textLeading();
+    }
+  }
+
+  if (baselineHacked) {
+    this.drawingContext.textBaseline = constants.BASELINE;
+  }
+
+  return p;
+};
+
+p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY) {
+
+  if (y >= maxY) {
+    return; // don't render lines beyond our maxY position
+  }
+
+  p.push(); // fix to #803
+
+  if (!this._isOpenType()) {  // a system/browser font
+
+    // no stroke unless specified by user
+    if (this._doStroke && this._strokeSet) {
+
+      this.drawingContext.strokeText(line, x, y);
+    }
+
+    if (this._doFill) {
+
+      // if fill hasn't been set by user, use default text fill
+      this.drawingContext.fillStyle =  this._fillSet ?
+        this.drawingContext.fillStyle : constants._DEFAULT_TEXT_FILL;
+
+      this.drawingContext.fillText(line, x, y);
+    }
+  }
+  else { // an opentype font, let it handle the rendering
+
+    this._textFont._renderPath(line, x, y, { renderer: this });
+  }
+
+  p.pop();
+
+  return p;
+};
+
+p5.Renderer2D.prototype.textWidth = function(s) {
+
+  if (this._isOpenType()) {
+
+    return this._textFont._textWidth(s);
+  }
+
+  return this.drawingContext.measureText(s).width;
+};
+
+p5.Renderer2D.prototype.textAlign = function(h, v) {
+
+  if (arguments.length) {
+
+    if (h === constants.LEFT ||
+      h === constants.RIGHT ||
+      h === constants.CENTER) {
+
+      this.drawingContext.textAlign = h;
+    }
+
+    if (v === constants.TOP ||
+      v === constants.BOTTOM ||
+      v === constants.CENTER ||
+      v === constants.BASELINE) {
+
+      if (v === constants.CENTER) {
+        this.drawingContext.textBaseline = constants._CTX_MIDDLE;
+      } else {
+        this.drawingContext.textBaseline = v;
+      }
+    }
+
+    return this._pInst;
+
+  } else {
+
+    var valign = this.drawingContext.textBaseline;
+
+    if (valign === constants._CTX_MIDDLE) {
+
+      valign = constants.CENTER;
+    }
+
+    return {
+
+      horizontal: this.drawingContext.textAlign,
+      vertical: valign
+    };
+  }
+};
+
+p5.Renderer2D.prototype._applyTextProperties = function() {
+
+  var font, p = this._pInst;
+
+  this._setProperty('_textAscent', null);
+  this._setProperty('_textDescent', null);
+
+  font = this._textFont;
+
+  if (this._isOpenType()) {
+
+    font = this._textFont.font.familyName;
+    this._setProperty('_textStyle', this._textFont.font.styleName);
+  }
+
+  this.drawingContext.font = this._textStyle + ' ' +
+  this._textSize + 'px ' + font;
+
+  return p;
+};
+
+
+//////////////////////////////////////////////
+// STRUCTURE
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.push = function() {
+  this.drawingContext.save();
+};
+
+p5.Renderer2D.prototype.pop = function() {
+  this.drawingContext.restore();
+};
+
+module.exports = p5.Renderer2D;
+
+},{"../image/filters":65,"./canvas":46,"./constants":47,"./core":48,"./p5.Renderer":54}],56:[function(_dereq_,module,exports){
+/**
+ * @module Rendering
+ * @submodule Rendering
+ * @for p5
+ */
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+_dereq_('./p5.Graphics');
+_dereq_('./p5.Renderer2D');
+_dereq_('../3d/p5.Renderer3D');
+var defaultId = 'defaultCanvas0'; // this gets set again in createCanvas
+
+/**
+ * Creates a canvas element in the document, and sets the dimensions of it
+ * in pixels. This method should be called only once at the start of setup.
+ * Calling createCanvas more than once in a sketch will result in very
+ * unpredicable behavior. If you want more than one drawing canvas
+ * you could use createGraphics (hidden by default but it can be shown).
+ * <br><br>
+ * The system variables width and height are set by the parameters passed
+ * to this function. If createCanvas() is not used, the window will be
+ * given a default size of 100x100 pixels.
+ *
+ * @method createCanvas
+ * @param  {Number} w width of the canvas
+ * @param  {Number} h height of the canvas
+ * @param  {String} [renderer] 'p2d' | 'webgl'
+ * @return {Object} canvas generated
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   createCanvas(100, 50);
+ *   background(153);
+ *   line(0, 0, width, height);
+ * }
+ * </code>
+ * </div>
+ */
+
+p5.prototype.createCanvas = function(w, h, renderer) {
+  //optional: renderer, otherwise defaults to p2d
+  var r = renderer || constants.P2D;
+  var isDefault, c;
+
+  //4th arg (isDefault) used when called onLoad,
+  //otherwise hidden to the public api
+  if(arguments[3]){
+    isDefault =
+    (typeof arguments[3] === 'boolean') ? arguments[3] : false;
+  }
+
+  if(r === constants.WEBGL){
+    c = document.getElementById(defaultId);
+    if(c){ //if defaultCanvas already exists
+      c.parentNode.removeChild(c); //replace the existing defaultCanvas
+    }
+    c = document.createElement('canvas');
+    c.id = defaultId;
+  }
+  else {
+    if (isDefault) {
+      c = document.createElement('canvas');
+      var i = 0;
+      while (document.getElementById('defaultCanvas'+i)) {
+        i++;
+      }
+      defaultId = 'defaultCanvas'+i;
+      c.id = defaultId;
+    } else { // resize the default canvas if new one is created
+      c = this.canvas;
+    }
+  }
+
+  // set to invisible if still in setup (to prevent flashing with manipulate)
+  if (!this._setupDone) {
+    c.className += ' p5_hidden'; // tag to show later
+    c.style.visibility='hidden';
+  }
+
+  if (this._userNode) { // user input node case
+    this._userNode.appendChild(c);
+  } else {
+    document.body.appendChild(c);
+  }
+
+
+
+  // Init our graphics renderer
+  //webgl mode
+  if (r === constants.WEBGL) {
+    this._setProperty('_renderer', new p5.Renderer3D(c, this, true));
+    this._isdefaultGraphics = true;
+  }
+  //P2D mode
+  else {
+    if (!this._isdefaultGraphics) {
+      this._setProperty('_renderer', new p5.Renderer2D(c, this, true));
+      this._isdefaultGraphics = true;
+    }
+  }
+  this._renderer.resize(w, h);
+  this._renderer._applyDefaults();
+  if (isDefault) { // only push once
+    this._elements.push(this._renderer);
+  }
+  return this._renderer;
+};
+
+/**
+ * Resizes the canvas to given width and height. The canvas will be cleared
+ * and draw will be called immediately, allowing the sketch to re-render itself
+ * in the resized canvas.
+ * @method resizeCanvas
+ * @example
+ * <div class="norender"><code>
+ * function setup() {
+ *   createCanvas(windowWidth, windowHeight);
+ * }
+ *
+ * function draw() {
+ *  background(0, 100, 200);
+ * }
+ *
+ * function windowResized() {
+ *   resizeCanvas(windowWidth, windowHeight);
+ * }
+ * </code></div>
+ */
+p5.prototype.resizeCanvas = function (w, h, noRedraw) {
+  if (this._renderer) {
+
+    // save canvas properties
+    var props = {};
+    for (var key in this.drawingContext) {
+      var val = this.drawingContext[key];
+      if (typeof val !== 'object' && typeof val !== 'function') {
+        props[key] = val;
+      }
+    }
+    this._renderer.resize(w, h);
+    // reset canvas properties
+    for (var savedKey in props) {
+      this.drawingContext[savedKey] = props[savedKey];
+    }
+    if (!noRedraw) {
+      this.redraw();
+    }
+  }
+};
+
+
+/**
+ * Removes the default canvas for a p5 sketch that doesn't
+ * require a canvas
+ * @method noCanvas
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   noCanvas();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noCanvas = function() {
+  if (this.canvas) {
+    this.canvas.parentNode.removeChild(this.canvas);
+  }
+};
+
+/**
+ * Creates and returns a new p5.Renderer object. Use this class if you need
+ * to draw into an off-screen graphics buffer. The two parameters define the
+ * width and height in pixels.
+ *
+ * @method createGraphics
+ * @param  {Number} w width of the offscreen graphics buffer
+ * @param  {Number} h height of the offscreen graphics buffer
+ * @param {String} renderer either 'p2d' or 'webgl'.
+ * undefined defaults to p2d
+ * @return {Object} offscreen graphics buffer
+ * @example
+ * <div>
+ * <code>
+ * var pg;
+ * function setup() {
+ *   createCanvas(100, 100);
+ *   pg = createGraphics(100, 100);
+ * }
+ * function draw() {
+ *   background(200);
+ *   pg.background(100);
+ *   pg.noStroke();
+ *   pg.ellipse(pg.width/2, pg.height/2, 50, 50);
+ *   image(pg, 50, 50);
+ *   image(pg, 0, 0, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.createGraphics = function(w, h, renderer){
+  return new p5.Graphics(w, h, renderer, this);
+};
+
+/**
+ * Blends the pixels in the display window according to the defined mode.
+ * There is a choice of the following modes to blend the source pixels (A)
+ * with the ones of pixels already in the display window (B):
+ * <ul>
+ * <li><code>BLEND</code> - linear interpolation of colours: C =
+ * A*factor + B. This is the default blending mode.</li>
+ * <li><code>ADD</code> - sum of A and B</li>
+ * <li><code>DARKEST</code> - only the darkest colour succeeds: C =
+ * min(A*factor, B).</li>
+ * <li><code>LIGHTEST</code> - only the lightest colour succeeds: C =
+ * max(A*factor, B).</li>
+ * <li><code>DIFFERENCE</code> - subtract colors from underlying image.</li>
+ * <li><code>EXCLUSION</code> - similar to <code>DIFFERENCE</code>, but less
+ * extreme.</li>
+ * <li><code>MULTIPLY</code> - multiply the colors, result will always be
+ * darker.</li>
+ * <li><code>SCREEN</code> - opposite multiply, uses inverse values of the
+ * colors.</li>
+ * <li><code>REPLACE</code> - the pixels entirely replace the others and
+ * don't utilize alpha (transparency) values.</li>
+ * <li><code>OVERLAY</code> - mix of <code>MULTIPLY</code> and <code>SCREEN
+ * </code>. Multiplies dark values, and screens light values.</li>
+ * <li><code>HARD_LIGHT</code> - <code>SCREEN</code> when greater than 50%
+ * gray, <code>MULTIPLY</code> when lower.</li>
+ * <li><code>SOFT_LIGHT</code> - mix of <code>DARKEST</code> and
+ * <code>LIGHTEST</code>. Works like <code>OVERLAY</code>, but not as harsh.
+ * </li>
+ * <li><code>DODGE</code> - lightens light tones and increases contrast,
+ * ignores darks.</li>
+ * <li><code>BURN</code> - darker areas are applied, increasing contrast,
+ * ignores lights.</li>
+ * </ul>
+ *
+ * @method blendMode
+ * @param  {String/Constant} mode blend mode to set for canvas
+ * @example
+ * <div>
+ * <code>
+ * blendMode(LIGHTEST);
+ * strokeWeight(30);
+ * stroke(80, 150, 255);
+ * line(25, 25, 75, 75);
+ * stroke(255, 50, 50);
+ * line(75, 25, 25, 75);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * blendMode(MULTIPLY);
+ * strokeWeight(30);
+ * stroke(80, 150, 255);
+ * line(25, 25, 75, 75);
+ * stroke(255, 50, 50);
+ * line(75, 25, 25, 75);
+ * </code>
+ * </div>
+ */
+p5.prototype.blendMode = function(mode) {
+  if (mode === constants.BLEND || mode === constants.DARKEST ||
+    mode === constants.LIGHTEST || mode === constants.DIFFERENCE ||
+    mode === constants.MULTIPLY || mode === constants.EXCLUSION ||
+    mode === constants.SCREEN || mode === constants.REPLACE ||
+    mode === constants.OVERLAY || mode === constants.HARD_LIGHT ||
+    mode === constants.SOFT_LIGHT || mode === constants.DODGE ||
+    mode === constants.BURN || mode === constants.ADD ||
+    mode === constants.NORMAL) {
+    this._renderer.blendMode(mode);
+  } else {
+    throw new Error('Mode '+mode+' not recognized.');
+  }
+};
+
+module.exports = p5;
+
+},{"../3d/p5.Renderer3D":36,"./constants":47,"./core":48,"./p5.Graphics":53,"./p5.Renderer2D":55}],57:[function(_dereq_,module,exports){
+
+// requestAnim shim layer by Paul Irish
+window.requestAnimationFrame = (function(){
+  return window.requestAnimationFrame      ||
+        window.webkitRequestAnimationFrame ||
+        window.mozRequestAnimationFrame    ||
+        window.oRequestAnimationFrame      ||
+        window.msRequestAnimationFrame     ||
+        function(callback, element){
+          // should '60' here be framerate?
+          window.setTimeout(callback, 1000 / 60);
+        };
+})();
+
+// use window.performance() to get max fast and accurate time in milliseconds
+window.performance = window.performance || {};
+window.performance.now = (function(){
+  var load_date = Date.now();
+  return window.performance.now        ||
+        window.performance.mozNow      ||
+        window.performance.msNow       ||
+        window.performance.oNow        ||
+        window.performance.webkitNow   ||
+        function () {
+          return Date.now() - load_date;
+        };
+})();
+
+/*
+// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+// http://my.opera.com/emoller/blog/2011/12/20/
+// requestanimationframe-for-smart-er-animating
+// requestAnimationFrame polyfill by Erik Möller
+// fixes from Paul Irish and Tino Zijdel
+(function() {
+  var lastTime = 0;
+  var vendors = ['ms', 'moz', 'webkit', 'o'];
+  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+    window.requestAnimationFrame =
+      window[vendors[x]+'RequestAnimationFrame'];
+    window.cancelAnimationFrame =
+      window[vendors[x]+'CancelAnimationFrame'] ||
+      window[vendors[x]+'CancelRequestAnimationFrame'];
+  }
+
+  if (!window.requestAnimationFrame) {
+    window.requestAnimationFrame = function(callback, element) {
+      var currTime = new Date().getTime();
+      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+      var id = window.setTimeout(function()
+        { callback(currTime + timeToCall); }, timeToCall);
+      lastTime = currTime + timeToCall;
+      return id;
+    };
+  }
+
+  if (!window.cancelAnimationFrame) {
+    window.cancelAnimationFrame = function(id) {
+      clearTimeout(id);
+    };
+  }
+}());
+*/
+
+/**
+ * shim for Uint8ClampedArray.slice
+ * (allows arrayCopy to work with pixels[])
+ * with thanks to http://halfpapstudios.com/blog/tag/html5-canvas/
+ * Enumerable set to false to protect for...in from
+ * Uint8ClampedArray.prototype pollution.
+ */
+(function () {
+  'use strict';
+  if (typeof Uint8ClampedArray !== 'undefined' &&
+      !Uint8ClampedArray.prototype.slice) {
+    Object.defineProperty(Uint8ClampedArray.prototype, 'slice', {
+      value: Array.prototype.slice,
+      writable: true, configurable: true, enumerable: false
+    });
+  }
+}());
+
+},{}],58:[function(_dereq_,module,exports){
+/**
+ * @module Structure
+ * @submodule Structure
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+
+p5.prototype.exit = function() {
+  throw 'exit() not implemented, see remove()';
+};
+/**
+ * Stops p5.js from continuously executing the code within draw().
+ * If loop() is called, the code in draw() begins to run continuously again.
+ * If using noLoop() in setup(), it should be the last line inside the block.
+ * <br><br>
+ * When noLoop() is used, it's not possible to manipulate or access the
+ * screen inside event handling functions such as mousePressed() or
+ * keyPressed(). Instead, use those functions to call redraw() or loop(),
+ * which will run draw(), which can update the screen properly. This means
+ * that when noLoop() has been called, no drawing can happen, and functions
+ * like saveFrame() or loadPixels() may not be used.
+ * <br><br>
+ * Note that if the sketch is resized, redraw() will be called to update
+ * the sketch, even after noLoop() has been specified. Otherwise, the sketch
+ * would enter an odd state until loop() was called.
+ *
+ * @method noLoop
+ * @example
+ * <div><code>
+ * function setup() {
+ *   createCanvas(100, 100);
+ *   background(200);
+ *   noLoop();
+ * }
+
+ * function draw() {
+ *   line(10, 10, 90, 90);
+ * }
+ * </code></div>
+ *
+ * <div><code>
+ * var x = 0;
+ * function setup() {
+ *   createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ *   background(204);
+ *   x = x + 0.1;
+ *   if (x > width) {
+ *     x = 0;
+ *   }
+ *   line(x, 0, x, height);
+ * }
+ *
+ * function mousePressed() {
+ *   noLoop();
+ * }
+ *
+ * function mouseReleased() {
+ *   loop();
+ * }
+ * </code></div>
+ */
+p5.prototype.noLoop = function() {
+  this._loop = false;
+};
+/**
+ * By default, p5.js loops through draw() continuously, executing the code
+ * within it. However, the draw() loop may be stopped by calling noLoop().
+ * In that case, the draw() loop can be resumed with loop().
+ *
+ * @method loop
+ * @example
+ * <div><code>
+ * var x = 0;
+ * function setup() {
+ *   createCanvas(100, 100);
+ *   noLoop();
+ * }
+ *
+ * function draw() {
+ *   background(204);
+ *   x = x + 0.1;
+ *   if (x > width) {
+ *     x = 0;
+ *   }
+ *   line(x, 0, x, height);
+ * }
+ *
+ * function mousePressed() {
+ *   loop();
+ * }
+ *
+ * function mouseReleased() {
+ *   noLoop();
+ * }
+ * </code></div>
+ */
+
+p5.prototype.loop = function() {
+  this._loop = true;
+  this._draw();
+};
+
+/**
+ * The push() function saves the current drawing style settings and
+ * transformations, while pop() restores these settings. Note that these
+ * functions are always used together. They allow you to change the style
+ * and transformation settings and later return to what you had. When a new
+ * state is started with push(), it builds on the current style and transform
+ * information. The push() and pop() functions can be embedded to provide
+ * more control. (See the second example for a demonstration.)
+ * <br><br>
+ * push() stores information related to the current transformation state
+ * and style settings controlled by the following functions: fill(),
+ * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(),
+ * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(),
+ * textFont(), textMode(), textSize(), textLeading().
+ *
+ * @method push
+ * @example
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33);  // Left circle
+ *
+ * push();  // Start a new drawing state
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * translate(50, 0);
+ * ellipse(0, 50, 33, 33);  // Middle circle
+ * pop();  // Restore original state
+ *
+ * ellipse(100, 50, 33, 33);  // Right circle
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33);  // Left circle
+ *
+ * push();  // Start a new drawing state
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * ellipse(33, 50, 33, 33);  // Left-middle circle
+ *
+ * push();  // Start another new drawing state
+ * stroke(0, 102, 153);
+ * ellipse(66, 50, 33, 33);  // Right-middle circle
+ * pop();  // Restore previous state
+ *
+ * pop();  // Restore original state
+ *
+ * ellipse(100, 50, 33, 33);  // Right circle
+ * </code>
+ * </div>
+ */
+p5.prototype.push = function () {
+  this._renderer.push();
+  this._styles.push({
+    _doStroke: this._renderer._doStroke,
+    _doFill: this._renderer._doFill,
+    _tint: this._renderer._tint,
+    _imageMode: this._renderer._imageMode,
+    _rectMode: this._renderer._rectMode,
+    _ellipseMode: this._renderer._ellipseMode,
+    _colorMode: this._renderer._colorMode,
+    _textFont: this._renderer._textFont,
+    _textLeading: this._renderer._textLeading,
+    _textSize: this._renderer._textSize,
+    _textStyle: this._renderer._textStyle
+  });
+};
+
+/**
+ * The push() function saves the current drawing style settings and
+ * transformations, while pop() restores these settings. Note that these
+ * functions are always used together. They allow you to change the style
+ * and transformation settings and later return to what you had. When a new
+ * state is started with push(), it builds on the current style and transform
+ * information. The push() and pop() functions can be embedded to provide
+ * more control. (See the second example for a demonstration.)
+ * <br><br>
+ * push() stores information related to the current transformation state
+ * and style settings controlled by the following functions: fill(),
+ * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(),
+ * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(),
+ * textFont(), textMode(), textSize(), textLeading().
+ *
+ * @method pop
+ * @example
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33);  // Left circle
+ *
+ * push();  // Start a new drawing state
+ * translate(50, 0);
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * ellipse(0, 50, 33, 33);  // Middle circle
+ * pop();  // Restore original state
+ *
+ * ellipse(100, 50, 33, 33);  // Right circle
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33);  // Left circle
+ *
+ * push();  // Start a new drawing state
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * ellipse(33, 50, 33, 33);  // Left-middle circle
+ *
+ * push();  // Start another new drawing state
+ * stroke(0, 102, 153);
+ * ellipse(66, 50, 33, 33);  // Right-middle circle
+ * pop();  // Restore previous state
+ *
+ * pop();  // Restore original state
+ *
+ * ellipse(100, 50, 33, 33);  // Right circle
+ * </code>
+ * </div>
+ */
+p5.prototype.pop = function () {
+  this._renderer.pop();
+  var lastS = this._styles.pop();
+  for(var prop in lastS){
+    this._renderer[prop] = lastS[prop];
+  }
+};
+
+p5.prototype.pushStyle = function() {
+  throw new Error('pushStyle() not used, see push()');
+};
+
+p5.prototype.popStyle = function() {
+  throw new Error('popStyle() not used, see pop()');
+};
+
+/**
+ *
+ * Executes the code within draw() one time. This functions allows the
+ * program to update the display window only when necessary, for example
+ * when an event registered by mousePressed() or keyPressed() occurs.
+ * <br><br>
+ * In structuring a program, it only makes sense to call redraw() within
+ * events such as mousePressed(). This is because redraw() does not run
+ * draw() immediately (it only sets a flag that indicates an update is
+ * needed).
+ * <br><br>
+ * The redraw() function does not work properly when called inside draw().
+ * To enable/disable animations, use loop() and noLoop().
+ *
+ * @method redraw
+ * @example
+ *   <div><code>
+ *     var x = 0;
+ *
+ *     function setup() {
+ *       createCanvas(100, 100);
+ *       noLoop();
+ *     }
+ *
+ *     function draw() {
+ *       background(204);
+ *       line(x, 0, x, height);
+ *     }
+ *
+ *     function mousePressed() {
+ *       x += 1;
+ *       redraw();
+ *     }
+ *   </code></div>
+ */
+p5.prototype.redraw = function () {
+  var userSetup = this.setup || window.setup;
+  var userDraw = this.draw || window.draw;
+  if (typeof userDraw === 'function') {
+    this.push();
+    if (typeof userSetup === 'undefined') {
+      this.scale(this._pixelDensity, this._pixelDensity);
+    }
+    var self = this;
+    this._registeredMethods.pre.forEach(function (f) {
+      f.call(self);
+    });
+    userDraw();
+    this._registeredMethods.post.forEach(function (f) {
+      f.call(self);
+    });
+    this.pop();
+  }
+};
+
+p5.prototype.size = function() {
+  var s = 'size() is not a valid p5 function, to set the size of the ';
+  s += 'drawing canvas, please use createCanvas() instead';
+  throw s;
+};
+
+
+module.exports = p5;
+
+},{"./core":48}],59:[function(_dereq_,module,exports){
+/**
+ * @module Transform
+ * @submodule Transform
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+/**
+ * Multiplies the current matrix by the one specified through the parameters.
+ * This is very slow because it will try to calculate the inverse of the
+ * transform, so avoid it whenever possible.
+ *
+ * @method applyMatrix
+ * @param  {Number} n00 numbers which define the 3x2 matrix to be multiplied
+ * @param  {Number} n01 numbers which define the 3x2 matrix to be multiplied
+ * @param  {Number} n02 numbers which define the 3x2 matrix to be multiplied
+ * @param  {Number} n10 numbers which define the 3x2 matrix to be multiplied
+ * @param  {Number} n11 numbers which define the 3x2 matrix to be multiplied
+ * @param  {Number} n12 numbers which define the 3x2 matrix to be multiplied
+ * @return {p5}         the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // Example in the works.
+ * </code>
+ * </div>
+ */
+p5.prototype.applyMatrix = function(n00, n01, n02, n10, n11, n12) {
+  this._renderer.applyMatrix(n00, n01, n02, n10, n11, n12);
+  return this;
+};
+
+p5.prototype.popMatrix = function() {
+  throw new Error('popMatrix() not used, see pop()');
+};
+
+p5.prototype.printMatrix = function() {
+  throw new Error('printMatrix() not implemented');
+};
+
+p5.prototype.pushMatrix = function() {
+  throw new Error('pushMatrix() not used, see push()');
+};
+
+/**
+ * Replaces the current matrix with the identity matrix.
+ *
+ * @method resetMatrix
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // Example in the works.
+ * </code>
+ * </div>
+ */
+p5.prototype.resetMatrix = function() {
+  this._renderer.resetMatrix();
+  return this;
+};
+
+/**
+ * Rotates a shape the amount specified by the angle parameter. This
+ * function accounts for angleMode, so angles can be entered in either
+ * RADIANS or DEGREES.
+ * <br><br>
+ * Objects are always rotated around their relative position to the
+ * origin and positive numbers rotate objects in a clockwise direction.
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI).
+ * All tranformations are reset when draw() begins again.
+ * <br><br>
+ * Technically, rotate() multiplies the current transformation matrix
+ * by a rotation matrix. This function can be further controlled by
+ * the push() and pop().
+ *
+ * @method rotate
+ * @param  {Number} angle the angle of rotation, specified in radians
+ *                        or degrees, depending on current angleMode
+ * @return {p5}           the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/2, height/2);
+ * rotate(PI/3.0);
+ * rect(-26, -26, 52, 52);
+ * </code>
+ * </div>
+ */
+p5.prototype.rotate = function() {
+  var r = arguments[0];
+  if (this._angleMode === constants.DEGREES) {
+    r = this.radians(r);
+  }
+  //in webgl mode
+  if(arguments.length > 1){
+    this._renderer.rotate(r, arguments[1]);
+  }
+  else {
+    this._renderer.rotate(r);
+  }
+  return this;
+};
+
+/**
+ * [rotateX description]
+ * @param  {[type]} rad [description]
+ * @return {[type]}     [description]
+ */
+p5.prototype.rotateX = function(rad) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  if (this._renderer.isP3D) {
+    this._validateParameters(
+      'rotateX',
+      args,
+      [
+        ['Number']
+      ]
+    );
+    this._renderer.rotateX(rad);
+  } else {
+    throw 'not supported in p2d. Please use webgl mode';
+  }
+  return this;
+};
+
+/**
+ * [rotateY description]
+ * @param  {[type]} rad [description]
+ * @return {[type]}     [description]
+ */
+p5.prototype.rotateY = function(rad) {
+  if (this._renderer.isP3D) {
+    var args = new Array(arguments.length);
+    for (var i = 0; i < args.length; ++i) {
+      args[i] = arguments[i];
+    }
+    this._validateParameters(
+      'rotateY',
+      args,
+      [
+        ['Number']
+      ]
+    );
+    this._renderer.rotateY(rad);
+  } else {
+    throw 'not supported in p2d. Please use webgl mode';
+  }
+  return this;
+};
+
+/**
+ * [rotateZ description]
+ * @param  {[type]} rad [description]
+ * @return {[type]}     [description]
+ */
+p5.prototype.rotateZ = function(rad) {
+  if (this._renderer.isP3D) {
+    var args = new Array(arguments.length);
+    for (var i = 0; i < args.length; ++i) {
+      args[i] = arguments[i];
+    }
+    this._validateParameters(
+      'rotateZ',
+      args,
+      [
+        ['Number']
+      ]
+    );
+    this._renderer.rotateZ(rad);
+  } else {
+    throw 'not supported in p2d. Please use webgl mode';
+  }
+  return this;
+};
+
+/**
+ * Increases or decreases the size of a shape by expanding and contracting
+ * vertices. Objects always scale from their relative origin to the
+ * coordinate system. Scale values are specified as decimal percentages.
+ * For example, the function call scale(2.0) increases the dimension of a
+ * shape by 200%.
+ * <br><br>
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function multiply the effect. For example, calling scale(2.0)
+ * and then scale(1.5) is the same as scale(3.0). If scale() is called
+ * within draw(), the transformation is reset when the loop begins again.
+ * <br><br>
+ * Using this fuction with the z parameter requires using P3D as a
+ * parameter for size(), as shown in the third example above. This function
+ * can be further controlled with push() and pop().
+ *
+ * @method scale
+ * @param  {Number | p5.Vector | Array} s
+ *                      percent to scale the object, or percentage to
+ *                      scale the object in the x-axis if multiple arguments
+ *                      are given
+ * @param  {Number} [y] percent to scale the object in the y-axis
+ * @param  {Number} [z] percent to scale the object in the z-axis (webgl only)
+ * @return {p5}         the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/2, height/2);
+ * rotate(PI/3.0);
+ * rect(-26, -26, 52, 52);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * rect(30, 20, 50, 50);
+ * scale(0.5, 1.3);
+ * rect(30, 20, 50, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.scale = function() {
+  var x,y,z;
+  var args = new Array(arguments.length);
+  for(var i = 0; i < args.length; i++) {
+    args[i] = arguments[i];
+  }
+  if(args[0] instanceof p5.Vector){
+    x = args[0].x;
+    y = args[0].y;
+    z = args[0].z;
+  }
+  else if(args[0] instanceof Array){
+    x = args[0][0];
+    y = args[0][1];
+    z = args[0][2] || 1;
+  }
+  else {
+    if(args.length === 1){
+      x = y = z = args[0];
+    }
+    else {
+      x = args[0];
+      y = args[1];
+      z = args[2] || 1;
+    }
+  }
+
+  if(this._renderer.isP3D){
+    this._renderer.scale.call(this._renderer, x,y,z);
+  }
+  else {
+    this._renderer.scale.call(this._renderer, x,y);
+  }
+  return this;
+};
+
+/**
+ * Shears a shape around the x-axis the amount specified by the angle
+ * parameter. Angles should be specified in the current angleMode.
+ * Objects are always sheared around their relative position to the origin
+ * and positive numbers shear objects in a clockwise direction.
+ * <br><br>
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI).
+ * If shearX() is called within the draw(), the transformation is reset when
+ * the loop begins again.
+ * <br><br>
+ * Technically, shearX() multiplies the current transformation matrix by a
+ * rotation matrix. This function can be further controlled by the
+ * push() and pop() functions.
+ *
+ * @method shearX
+ * @param  {Number} angle angle of shear specified in radians or degrees,
+ *                        depending on current angleMode
+ * @return {p5}           the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/4, height/4);
+ * shearX(PI/4.0);
+ * rect(0, 0, 30, 30);
+ * </code>
+ * </div>
+ */
+p5.prototype.shearX = function(angle) {
+  if (this._angleMode === constants.DEGREES) {
+    angle = this.radians(angle);
+  }
+  this._renderer.shearX(angle);
+  return this;
+};
+
+/**
+ * Shears a shape around the y-axis the amount specified by the angle
+ * parameter. Angles should be specified in the current angleMode. Objects
+ * are always sheared around their relative position to the origin and
+ * positive numbers shear objects in a clockwise direction.
+ * <br><br>
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI). If
+ * shearY() is called within the draw(), the transformation is reset when
+ * the loop begins again.
+ * <br><br>
+ * Technically, shearY() multiplies the current transformation matrix by a
+ * rotation matrix. This function can be further controlled by the
+ * push() and pop() functions.
+ *
+ * @method shearY
+ * @param  {Number} angle angle of shear specified in radians or degrees,
+ *                        depending on current angleMode
+ * @return {p5}           the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/4, height/4);
+ * shearY(PI/4.0);
+ * rect(0, 0, 30, 30);
+ * </code>
+ * </div>
+ */
+p5.prototype.shearY = function(angle) {
+  if (this._angleMode === constants.DEGREES) {
+    angle = this.radians(angle);
+  }
+  this._renderer.shearY(angle);
+  return this;
+};
+
+/**
+ * Specifies an amount to displace objects within the display window.
+ * The x parameter specifies left/right translation, the y parameter
+ * specifies up/down translation.
+ * <br><br>
+ * Transformations are cumulative and apply to everything that happens after
+ * and subsequent calls to the function accumulates the effect. For example,
+ * calling translate(50, 0) and then translate(20, 0) is the same as
+ * translate(70, 0). If translate() is called within draw(), the
+ * transformation is reset when the loop begins again. This function can be
+ * further controlled by using push() and pop().
+ *
+ * @method translate
+ * @param  {Number} x left/right translation
+ * @param  {Number} y up/down translation
+ * @return {p5}       the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(30, 20);
+ * rect(0, 0, 55, 55);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * rect(0, 0, 55, 55);  // Draw rect at original 0,0
+ * translate(30, 20);
+ * rect(0, 0, 55, 55);  // Draw rect at new 0,0
+ * translate(14, 14);
+ * rect(0, 0, 55, 55);  // Draw rect at new 0,0
+ * </code>
+ * </div>
+ */
+p5.prototype.translate = function(x, y, z) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+
+  if (this._renderer.isP3D) {
+    this._validateParameters(
+      'translate',
+      args,
+      [
+        //p3d
+        ['Number', 'Number', 'Number']
+      ]
+    );
+    this._renderer.translate(x, y, z);
+  } else {
+    this._validateParameters(
+      'translate',
+      args,
+      [
+        //p2d
+        ['Number', 'Number']
+      ]
+    );
+    this._renderer.translate(x, y);
+  }
+  return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],60:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule Vertex
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+var shapeKind = null;
+var vertices = [];
+var contourVertices = [];
+var isBezier = false;
+var isCurve = false;
+var isQuadratic = false;
+var isContour = false;
+
+/**
+ * Use the beginContour() and endContour() functions to create negative
+ * shapes within shapes such as the center of the letter 'O'. beginContour()
+ * begins recording vertices for the shape and endContour() stops recording.
+ * The vertices that define a negative shape must "wind" in the opposite
+ * direction from the exterior shape. First draw vertices for the exterior
+ * clockwise order, then for internal shapes, draw vertices
+ * shape in counter-clockwise.
+ * <br><br>
+ * These functions can only be used within a beginShape()/endShape() pair and
+ * transformations such as translate(), rotate(), and scale() do not work
+ * within a beginContour()/endContour() pair. It is also not possible to use
+ * other shapes, such as ellipse() or rect() within.
+ *
+ * @method beginContour
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(50, 50);
+ * stroke(255, 0, 0);
+ * beginShape();
+ * // Exterior part of shape, clockwise winding
+ * vertex(-40, -40);
+ * vertex(40, -40);
+ * vertex(40, 40);
+ * vertex(-40, 40);
+ * // Interior part of shape, counter-clockwise winding
+ * beginContour();
+ * vertex(-20, -20);
+ * vertex(-20, 20);
+ * vertex(20, 20);
+ * vertex(20, -20);
+ * endContour();
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ */
+p5.prototype.beginContour = function() {
+  contourVertices = [];
+  isContour = true;
+  return this;
+};
+
+/**
+ * Using the beginShape() and endShape() functions allow creating more
+ * complex forms. beginShape() begins recording vertices for a shape and
+ * endShape() stops recording. The value of the kind parameter tells it which
+ * types of shapes to create from the provided vertices. With no mode
+ * specified, the shape can be any irregular polygon.
+ * <br><br>
+ * The parameters available for beginShape() are POINTS, LINES, TRIANGLES,
+ * TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP. After calling the
+ * beginShape() function, a series of vertex() commands must follow. To stop
+ * drawing the shape, call endShape(). Each shape will be outlined with the
+ * current stroke color and filled with the fill color.
+ * <br><br>
+ * Transformations such as translate(), rotate(), and scale() do not work
+ * within beginShape(). It is also not possible to use other shapes, such as
+ * ellipse() or rect() within beginShape().
+ *
+ * @method beginShape
+ * @param  {Number/Constant} kind either POINTS, LINES, TRIANGLES,
+ *                                TRIANGLE_FAN, TRIANGLE_STRIP, QUADS,
+ *                                or QUAD_STRIP
+ * @return {Object}               the p5 object
+ * @example
+ * <div>
+ * <code>
+ * beginShape();
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // currently not working
+ * beginShape(POINTS);
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(LINES);
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(TRIANGLES);
+ * vertex(30, 75);
+ * vertex(40, 20);
+ * vertex(50, 75);
+ * vertex(60, 20);
+ * vertex(70, 75);
+ * vertex(80, 20);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(TRIANGLE_STRIP);
+ * vertex(30, 75);
+ * vertex(40, 20);
+ * vertex(50, 75);
+ * vertex(60, 20);
+ * vertex(70, 75);
+ * vertex(80, 20);
+ * vertex(90, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(TRIANGLE_FAN);
+ * vertex(57.5, 50);
+ * vertex(57.5, 15);
+ * vertex(92, 50);
+ * vertex(57.5, 85);
+ * vertex(22, 50);
+ * vertex(57.5, 15);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(QUADS);
+ * vertex(30, 20);
+ * vertex(30, 75);
+ * vertex(50, 75);
+ * vertex(50, 20);
+ * vertex(65, 20);
+ * vertex(65, 75);
+ * vertex(85, 75);
+ * vertex(85, 20);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(QUAD_STRIP);
+ * vertex(30, 20);
+ * vertex(30, 75);
+ * vertex(50, 20);
+ * vertex(50, 75);
+ * vertex(65, 20);
+ * vertex(65, 75);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape();
+ * vertex(20, 20);
+ * vertex(40, 20);
+ * vertex(40, 40);
+ * vertex(60, 40);
+ * vertex(60, 60);
+ * vertex(20, 60);
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ */
+p5.prototype.beginShape = function(kind) {
+  if (kind === constants.POINTS ||
+    kind === constants.LINES ||
+    kind === constants.TRIANGLES ||
+    kind === constants.TRIANGLE_FAN ||
+    kind === constants.TRIANGLE_STRIP ||
+    kind === constants.QUADS ||
+    kind === constants.QUAD_STRIP) {
+    shapeKind = kind;
+  } else {
+    shapeKind = null;
+  }
+  if(this._renderer.isP3D){
+    this._renderer.beginShape(kind);
+  } else {
+    vertices = [];
+    contourVertices = [];
+  }
+  return this;
+};
+
+/**
+ * Specifies vertex coordinates for Bezier curves. Each call to
+ * bezierVertex() defines the position of two control points and
+ * one anchor point of a Bezier curve, adding a new segment to a
+ * line or shape.
+ * <br><br>
+ * The first time bezierVertex() is used within a
+ * beginShape() call, it must be prefaced with a call to vertex()
+ * to set the first anchor point. This function must be used between
+ * beginShape() and endShape() and only when there is no MODE
+ * parameter specified to beginShape().
+ *
+ * @method bezierVertex
+ * @param  {Number} x2 x-coordinate for the first control point
+ * @param  {Number} y2 y-coordinate for the first control point
+ * @param  {Number} x3 x-coordinate for the second control point
+ * @param  {Number} y3 y-coordinate for the second control point
+ * @param  {Number} x4 x-coordinate for the anchor point
+ * @param  {Number} y4 y-coordinate for the anchor point
+ * @return {Object}    the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * vertex(30, 20);
+ * bezierVertex(80, 0, 80, 75, 30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape();
+ * vertex(30, 20);
+ * bezierVertex(80, 0, 80, 75, 30, 75);
+ * bezierVertex(50, 80, 60, 25, 30, 20);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierVertex = function(x2, y2, x3, y3, x4, y4) {
+  if (vertices.length === 0) {
+    throw 'vertex() must be used once before calling bezierVertex()';
+  } else {
+    isBezier = true;
+    var vert = [];
+    for (var i = 0; i < arguments.length; i++) {
+      vert[i] = arguments[i];
+    }
+    vert.isVert = false;
+    if (isContour) {
+      contourVertices.push(vert);
+    } else {
+      vertices.push(vert);
+    }
+  }
+  return this;
+};
+
+/**
+ * Specifies vertex coordinates for curves. This function may only
+ * be used between beginShape() and endShape() and only when there
+ * is no MODE parameter specified to beginShape().
+ * <br><br>
+ * The first and last points in a series of curveVertex() lines will be used to
+ * guide the beginning and end of a the curve. A minimum of four
+ * points is required to draw a tiny curve between the second and
+ * third points. Adding a fifth point with curveVertex() will draw
+ * the curve between the second, third, and fourth points. The
+ * curveVertex() function is an implementation of Catmull-Rom
+ * splines.
+ *
+ * @method curveVertex
+ * @param {Number} x x-coordinate of the vertex
+ * @param {Number} y y-coordinate of the vertex
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * curveVertex(84,  91);
+ * curveVertex(84,  91);
+ * curveVertex(68,  19);
+ * curveVertex(21,  17);
+ * curveVertex(32, 100);
+ * curveVertex(32, 100);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.curveVertex = function(x,y) {
+  isCurve = true;
+  this.vertex(x, y);
+  return this;
+};
+
+/**
+ * Use the beginContour() and endContour() functions to create negative
+ * shapes within shapes such as the center of the letter 'O'. beginContour()
+ * begins recording vertices for the shape and endContour() stops recording.
+ * The vertices that define a negative shape must "wind" in the opposite
+ * direction from the exterior shape. First draw vertices for the exterior
+ * clockwise order, then for internal shapes, draw vertices
+ * shape in counter-clockwise.
+ * <br><br>
+ * These functions can only be used within a beginShape()/endShape() pair and
+ * transformations such as translate(), rotate(), and scale() do not work
+ * within a beginContour()/endContour() pair. It is also not possible to use
+ * other shapes, such as ellipse() or rect() within.
+ *
+ * @method endContour
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(50, 50);
+ * stroke(255, 0, 0);
+ * beginShape();
+ * // Exterior part of shape, clockwise winding
+ * vertex(-40, -40);
+ * vertex(40, -40);
+ * vertex(40, 40);
+ * vertex(-40, 40);
+ * // Interior part of shape, counter-clockwise winding
+ * beginContour();
+ * vertex(-20, -20);
+ * vertex(-20, 20);
+ * vertex(20, 20);
+ * vertex(20, -20);
+ * endContour();
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ */
+p5.prototype.endContour = function() {
+  var vert = contourVertices[0].slice(); // copy all data
+  vert.isVert = contourVertices[0].isVert;
+  vert.moveTo = false;
+  contourVertices.push(vert);
+
+  vertices.push(vertices[0]);
+  for (var i = 0; i < contourVertices.length; i++) {
+    vertices.push(contourVertices[i]);
+  }
+  return this;
+};
+
+/**
+ * The endShape() function is the companion to beginShape() and may only be
+ * called after beginShape(). When endshape() is called, all of image data
+ * defined since the previous call to beginShape() is written into the image
+ * buffer. The constant CLOSE as the value for the MODE parameter to close
+ * the shape (to connect the beginning and the end).
+ *
+ * @method endShape
+ * @param  {Number/Constant} mode use CLOSE to close the shape
+ * @return {Object}               the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ *
+ * beginShape();
+ * vertex(20, 20);
+ * vertex(45, 20);
+ * vertex(45, 80);
+ * endShape(CLOSE);
+ *
+ * beginShape();
+ * vertex(50, 20);
+ * vertex(75, 20);
+ * vertex(75, 80);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.endShape = function(mode) {
+  if(this._renderer.isP3D){
+    this._renderer.endShape();
+  }else{
+    if (vertices.length === 0) { return this; }
+    if (!this._renderer._doStroke && !this._renderer._doFill) { return this; }
+
+    var closeShape = mode === constants.CLOSE;
+
+    // if the shape is closed, the first element is also the last element
+    if (closeShape && !isContour) {
+      vertices.push(vertices[0]);
+    }
+
+    this._renderer.endShape(mode, vertices, isCurve, isBezier,
+      isQuadratic, isContour, shapeKind);
+
+    // Reset some settings
+    isCurve = false;
+    isBezier = false;
+    isQuadratic = false;
+    isContour = false;
+
+    // If the shape is closed, the first element was added as last element.
+    // We must remove it again to prevent the list of vertices from growing
+    // over successive calls to endShape(CLOSE)
+    if (closeShape) {
+      vertices.pop();
+    }
+  }
+  return this;
+};
+
+/**
+ * Specifies vertex coordinates for quadratic Bezier curves. Each call to
+ * quadraticVertex() defines the position of one control points and one
+ * anchor point of a Bezier curve, adding a new segment to a line or shape.
+ * The first time quadraticVertex() is used within a beginShape() call, it
+ * must be prefaced with a call to vertex() to set the first anchor point.
+ * This function must be used between beginShape() and endShape() and only
+ * when there is no MODE parameter specified to beginShape().
+ *
+ * @method quadraticVertex
+ * @param  {Number} cx x-coordinate for the control point
+ * @param  {Number} cy y-coordinate for the control point
+ * @param  {Number} x3 x-coordinate for the anchor point
+ * @param  {Number} y3 y-coordinate for the anchor point
+ * @return {Object}    the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(4);
+ * beginShape();
+ * vertex(20, 20);
+ * quadraticVertex(80, 20, 50, 50);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(4);
+ * beginShape();
+ * vertex(20, 20);
+ * quadraticVertex(80, 20, 50, 50);
+ * quadraticVertex(20, 80, 80, 80);
+ * vertex(80, 60);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.quadraticVertex = function(cx, cy, x3, y3) {
+  //if we're drawing a contour, put the points into an
+  // array for inside drawing
+  if(this._contourInited) {
+    var pt = {};
+    pt.x = cx;
+    pt.y = cy;
+    pt.x3 = x3;
+    pt.y3 = y3;
+    pt.type = constants.QUADRATIC;
+    this._contourVertices.push(pt);
+
+    return this;
+  }
+  if (vertices.length > 0) {
+    isQuadratic = true;
+    var vert = [];
+    for (var i = 0; i < arguments.length; i++) {
+      vert[i] = arguments[i];
+    }
+    vert.isVert = false;
+    if (isContour) {
+      contourVertices.push(vert);
+    } else {
+      vertices.push(vert);
+    }
+  } else {
+    throw 'vertex() must be used once before calling quadraticVertex()';
+  }
+  return this;
+};
+
+/**
+ * All shapes are constructed by connecting a series of vertices. vertex()
+ * is used to specify the vertex coordinates for points, lines, triangles,
+ * quads, and polygons. It is used exclusively within the beginShape() and
+ * endShape() functions.
+ *
+ * @method vertex
+ * @param  {Number} x x-coordinate of the vertex
+ * @param  {Number} y y-coordinate of the vertex
+ * @return {Object}   the p5 object
+ * @example
+ * <div>
+ * <code>
+ * beginShape(POINTS);
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.vertex = function(x, y, moveTo) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  if(this._renderer.isP3D){
+    this._validateParameters(
+      'vertex',
+      args,
+      [
+        ['Number', 'Number', 'Number']
+      ]
+    );
+    this._renderer.vertex
+    (arguments[0], arguments[1], arguments[2]);
+  }else{
+    this._validateParameters(
+      'vertex',
+      args,
+      [
+        ['Number', 'Number'],
+        ['Number', 'Number', 'Number']
+      ]
+    );
+    var vert = [];
+    vert.isVert = true;
+    vert[0] = x;
+    vert[1] = y;
+    vert[2] = 0;
+    vert[3] = 0;
+    vert[4] = 0;
+    vert[5] = this._renderer._getFill();
+    vert[6] = this._renderer._getStroke();
+
+    if (moveTo) {
+      vert.moveTo = moveTo;
+    }
+    if (isContour) {
+      if (contourVertices.length === 0) {
+        vert.moveTo = true;
+      }
+      contourVertices.push(vert);
+    } else {
+      vertices.push(vert);
+    }
+  }
+  return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],61:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Acceleration
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * The system variable deviceOrientation always contains the orientation of
+ * the device. The value of this variable will either be set 'landscape'
+ * or 'portrait'. If no data is available it will be set to 'undefined'.
+ *
+ * @property deviceOrientation
+ */
+p5.prototype.deviceOrientation = undefined;
+
+/**
+ * The system variable accelerationX always contains the acceleration of the
+ * device along the x axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationX
+ */
+p5.prototype.accelerationX = 0;
+
+/**
+ * The system variable accelerationY always contains the acceleration of the
+ * device along the y axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationY
+ */
+p5.prototype.accelerationY = 0;
+
+/**
+ * The system variable accelerationZ always contains the acceleration of the
+ * device along the z axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationZ
+ */
+p5.prototype.accelerationZ = 0;
+
+/**
+ * The system variable pAccelerationX always contains the acceleration of the
+ * device along the x axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationX
+ */
+p5.prototype.pAccelerationX = 0;
+
+/**
+ * The system variable pAccelerationY always contains the acceleration of the
+ * device along the y axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationY
+ */
+p5.prototype.pAccelerationY = 0;
+
+/**
+ * The system variable pAccelerationZ always contains the acceleration of the
+ * device along the z axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationZ
+ */
+p5.prototype.pAccelerationZ = 0;
+
+/**
+ * _updatePAccelerations updates the pAcceleration values
+ *
+ * @private
+ */
+p5.prototype._updatePAccelerations = function(){
+  this._setProperty('pAccelerationX', this.accelerationX);
+  this._setProperty('pAccelerationY', this.accelerationY);
+  this._setProperty('pAccelerationZ', this.accelerationZ);
+};
+
+/**
+ * The system variable rotationX always contains the rotation of the
+ * device along the x axis. Value is represented as 0 to +/-180 degrees.
+ * <br><br>
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   //rotateZ(radians(rotationZ));
+ *   rotateX(radians(rotationX));
+ *   //rotateY(radians(rotationY));
+ *   box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ *
+ * @property rotationX
+ */
+p5.prototype.rotationX = 0;
+
+/**
+ * The system variable rotationY always contains the rotation of the
+ * device along the y axis. Value is represented as 0 to +/-90 degrees.
+ * <br><br>
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   //rotateZ(radians(rotationZ));
+ *   //rotateX(radians(rotationX));
+ *   rotateY(radians(rotationY));
+ *   box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ *
+ * @property rotationY
+ */
+p5.prototype.rotationY = 0;
+
+/**
+ * The system variable rotationZ always contains the rotation of the
+ * device along the z axis. Value is represented as 0 to 359 degrees.
+ * <br><br>
+ * Unlike rotationX and rotationY, this variable is available for devices
+ * with a built-in compass only.
+ * <br><br>
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ *   createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ *   background(200);
+ *   rotateZ(radians(rotationZ));
+ *   //rotateX(radians(rotationX));
+ *   //rotateY(radians(rotationY));
+ *   box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ *
+ * @property rotationZ
+ */
+p5.prototype.rotationZ = 0;
+
+/**
+ * The system variable pRotationX always contains the rotation of the
+ * device along the x axis in the frame previous to the current frame. Value
+ * is represented as 0 to +/-180 degrees.
+ * <br><br>
+ * pRotationX can also be used with rotationX to determine the rotate
+ * direction of the device along the X-axis.
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // A simple if statement looking at whether
+ * // rotationX - pRotationX < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely neccessary but the logic
+ * // will be different in that case.
+ *
+ * var rX = rotationX + 180;
+ * var pRX = pRotationX + 180;
+ *
+ * if ((rX - pRX > 0 && rX - pRX < 270)|| rX - pRX < -270){
+ *   rotateDirection = 'clockwise';
+ * } else if (rX - pRX < 0 || rX - pRX > 270){
+ *   rotateDirection = 'counter-clockwise';
+ * }
+ * </code>
+ * </div>
+ *
+ * @property pRotationX
+ */
+p5.prototype.pRotationX = 0;
+
+/**
+ * The system variable pRotationY always contains the rotation of the
+ * device along the y axis in the frame previous to the current frame. Value
+ * is represented as 0 to +/-90 degrees.
+ * <br><br>
+ * pRotationY can also be used with rotationY to determine the rotate
+ * direction of the device along the Y-axis.
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // A simple if statement looking at whether
+ * // rotationY - pRotationY < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely neccessary but the logic
+ * // will be different in that case.
+ *
+ * var rY = rotationY + 180;
+ * var pRY = pRotationY + 180;
+ *
+ * if ((rY - pRY > 0 && rY - pRY < 270)|| rY - pRY < -270){
+ *   rotateDirection = 'clockwise';
+ * } else if (rY - pRY < 0 || rY - pRY > 270){
+ *   rotateDirection = 'counter-clockwise';
+ * }
+ * </code>
+ * </div>
+ *
+ * @property pRotationY
+ */
+p5.prototype.pRotationY = 0;
+
+/**
+ * The system variable pRotationZ always contains the rotation of the
+ * device along the z axis in the frame previous to the current frame. Value
+ * is represented as 0 to 359 degrees.
+ * <br><br>
+ * pRotationZ can also be used with rotationZ to determine the rotate
+ * direction of the device along the Z-axis.
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // A simple if statement looking at whether
+ * // rotationZ - pRotationZ < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * if ((rotationZ - pRotationZ > 0 &&
+ *   rotationZ - pRotationZ < 270)||
+ *   rotationZ - pRotationZ < -270){
+ *
+ *   rotateDirection = 'clockwise';
+ *
+ * } else if (rotationZ - pRotationZ < 0 ||
+ *   rotationZ - pRotationZ > 270){
+ *
+ *   rotateDirection = 'counter-clockwise';
+ *
+ * }
+ * </code>
+ * </div>
+ *
+ * @property pRotationZ
+ */
+p5.prototype.pRotationZ = 0;
+
+var startAngleX = 0;
+var startAngleY = 0;
+var startAngleZ = 0;
+
+var rotateDirectionX = 'clockwise';
+var rotateDirectionY = 'clockwise';
+var rotateDirectionZ = 'clockwise';
+
+var pRotateDirectionX;
+var pRotateDirectionY;
+var pRotateDirectionZ;
+
+p5.prototype._updatePRotations = function(){
+  this._setProperty('pRotationX', this.rotationX);
+  this._setProperty('pRotationY', this.rotationY);
+  this._setProperty('pRotationZ', this.rotationZ);
+};
+
+p5.prototype.turnAxis = undefined;
+
+var move_threshold = 0.5;
+var shake_threshold = 30;
+
+/**
+ * The setMoveThreshold() function is used to set the movement threshold for
+ * the deviceMoved() function. The default threshold is set to 0.5.
+ *
+ * @method setMoveThreshold
+ * @param {number} value The threshold value
+ */
+p5.prototype.setMoveThreshold = function(val){
+  if(typeof val === 'number'){
+    move_threshold = val;
+  }
+};
+
+/**
+ * The setShakeThreshold() function is used to set the movement threshold for
+ * the deviceShaken() function. The default threshold is set to 30.
+ *
+ * @method setShakeThreshold
+ * @param {number} value The threshold value
+ */
+p5.prototype.setShakeThreshold = function(val){
+  if(typeof val === 'number'){
+    shake_threshold = val;
+  }
+};
+
+/**
+ * The deviceMoved() function is called when the device is moved by more than
+ * the threshold value along X, Y or Z axis. The default threshold is set to
+ * 0.5.
+ * @method deviceMoved
+ * @example
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Move the device around
+ * // to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function deviceMoved() {
+ *   value = value + 5;
+ *   if (value > 255) {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+
+/**
+ * The deviceTurned() function is called when the device rotates by
+ * more than 90 degrees continuously.
+ * <br><br>
+ * The axis that triggers the deviceTurned() method is stored in the turnAxis
+ * variable. The deviceTurned() method can be locked to trigger on any axis:
+ * X, Y or Z by comparing the turnAxis variable to 'X', 'Y' or 'Z'.
+ *
+ * @method deviceTurned
+ * @example
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees
+ * // to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function deviceTurned() {
+ *   if (value == 0){
+ *     value = 255
+ *   } else if (value == 255) {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees in the
+ * // X-axis to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function deviceTurned() {
+ *   if (turnAxis == 'X'){
+ *     if (value == 0){
+ *       value = 255
+ *     } else if (value == 255) {
+ *       value = 0;
+ *     }
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+
+/**
+ * The deviceShaken() function is called when the device total acceleration
+ * changes of accelerationX and accelerationY values is more than
+ * the threshold value. The default threshold is set to 30.
+ * @method deviceShaken
+ * @example
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Shake the device to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function deviceShaken() {
+ *   value = value + 5;
+ *   if (value > 255) {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+
+p5.prototype._ondeviceorientation = function (e) {
+  this._updatePRotations();
+  this._setProperty('rotationX', e.beta);
+  this._setProperty('rotationY', e.gamma);
+  this._setProperty('rotationZ', e.alpha);
+  this._handleMotion();
+};
+p5.prototype._ondevicemotion = function (e) {
+  this._updatePAccelerations();
+  this._setProperty('accelerationX', e.acceleration.x * 2);
+  this._setProperty('accelerationY', e.acceleration.y * 2);
+  this._setProperty('accelerationZ', e.acceleration.z * 2);
+  this._handleMotion();
+};
+p5.prototype._handleMotion = function() {
+  if (window.orientation === 90 || window.orientation === -90) {
+    this._setProperty('deviceOrientation', 'landscape');
+  } else if (window.orientation === 0) {
+    this._setProperty('deviceOrientation', 'portrait');
+  } else if (window.orientation === undefined) {
+    this._setProperty('deviceOrientation', 'undefined');
+  }
+  var deviceMoved = this.deviceMoved || window.deviceMoved;
+  if (typeof deviceMoved === 'function') {
+    if (Math.abs(this.accelerationX - this.pAccelerationX) > move_threshold ||
+      Math.abs(this.accelerationY - this.pAccelerationY) > move_threshold ||
+      Math.abs(this.accelerationZ - this.pAccelerationZ) > move_threshold) {
+      deviceMoved();
+    }
+  }
+  var deviceTurned = this.deviceTurned || window.deviceTurned;
+  if (typeof deviceTurned === 'function') {
+    // The angles given by rotationX etc is from range -180 to 180.
+    // The following will convert them to 0 to 360 for ease of calculation
+    // of cases when the angles wrapped around.
+    // _startAngleX will be converted back at the end and updated.
+    var wRX = this.rotationX + 180;
+    var wPRX = this.pRotationX + 180;
+    var wSAX = startAngleX + 180;
+    if ((wRX - wPRX > 0 && wRX - wPRX < 270)|| wRX - wPRX < -270){
+      rotateDirectionX = 'clockwise';
+    } else if (wRX - wPRX < 0 || wRX - wPRX > 270){
+      rotateDirectionX = 'counter-clockwise';
+    }
+    if (rotateDirectionX !== pRotateDirectionX){
+      wSAX = wRX;
+    }
+    if (Math.abs(wRX - wSAX) > 90 && Math.abs(wRX - wSAX) < 270){
+      wSAX = wRX;
+      this._setProperty('turnAxis', 'X');
+      deviceTurned();
+    }
+    pRotateDirectionX = rotateDirectionX;
+    startAngleX = wSAX - 180;
+
+    // Y-axis is identical to X-axis except for changing some names.
+    var wRY = this.rotationY + 180;
+    var wPRY = this.pRotationY + 180;
+    var wSAY = startAngleY + 180;
+    if ((wRY - wPRY > 0 && wRY - wPRY < 270)|| wRY - wPRY < -270){
+      rotateDirectionY = 'clockwise';
+    } else if (wRY - wPRY < 0 || wRY - this.pRotationY > 270){
+      rotateDirectionY = 'counter-clockwise';
+    }
+    if (rotateDirectionY !== pRotateDirectionY){
+      wSAY = wRY;
+    }
+    if (Math.abs(wRY - wSAY) > 90 && Math.abs(wRY - wSAY) < 270){
+      wSAY = wRY;
+      this._setProperty('turnAxis', 'Y');
+      deviceTurned();
+    }
+    pRotateDirectionY = rotateDirectionY;
+    startAngleY = wSAY - 180;
+
+    // Z-axis is already in the range 0 to 360
+    // so no conversion is needed.
+    if ((this.rotationZ - this.pRotationZ > 0 &&
+      this.rotationZ - this.pRotationZ < 270)||
+      this.rotationZ - this.pRotationZ < -270){
+      rotateDirectionZ = 'clockwise';
+    } else if (this.rotationZ - this.pRotationZ < 0 ||
+      this.rotationZ - this.pRotationZ > 270){
+      rotateDirectionZ = 'counter-clockwise';
+    }
+    if (rotateDirectionZ !== pRotateDirectionZ){
+      startAngleZ = this.rotationZ;
+    }
+    if (Math.abs(this.rotationZ - startAngleZ) > 90 &&
+      Math.abs(this.rotationZ - startAngleZ) < 270){
+      startAngleZ = this.rotationZ;
+      this._setProperty('turnAxis', 'Z');
+      deviceTurned();
+    }
+    pRotateDirectionZ = rotateDirectionZ;
+    this._setProperty('turnAxis', undefined);
+  }
+  var deviceShaken = this.deviceShaken || window.deviceShaken;
+  if (typeof deviceShaken === 'function') {
+    var accelerationChangeX;
+    var accelerationChangeY;
+    // Add accelerationChangeZ if acceleration change on Z is needed
+    if (this.pAccelerationX !== null) {
+      accelerationChangeX = Math.abs(this.accelerationX - this.pAccelerationX);
+      accelerationChangeY = Math.abs(this.accelerationY - this.pAccelerationY);
+    }
+    if (accelerationChangeX + accelerationChangeY > shake_threshold) {
+      deviceShaken();
+    }
+  }
+};
+
+
+module.exports = p5;
+
+},{"../core/core":48}],62:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Keyboard
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Holds the key codes of currently pressed keys.
+ * @private
+ */
+var downKeys = {};
+
+/**
+ * The boolean system variable keyIsPressed is true if any key is pressed
+ * and false if no keys are pressed.
+ *
+ * @property keyIsPressed
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ *   if (keyIsPressed === true) {
+ *     fill(0);
+ *   } else {
+ *     fill(255);
+ *   }
+ *   rect(25, 25, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.isKeyPressed = false;
+p5.prototype.keyIsPressed = false; // khan
+
+/**
+ * The system variable key always contains the value of the most recent
+ * key on the keyboard that was typed. To get the proper capitalization, it
+ * is best to use it within keyTyped(). For non-ASCII keys, use the keyCode
+ * variable.
+ *
+ * @property key
+ * @example
+ * <div><code>
+ * // Click any key to display it!
+ * // (Not Guaranteed to be Case Sensitive)
+ * function setup() {
+ *   fill(245, 123, 158);
+ *   textSize(50);
+ * }
+ *
+ * function draw() {
+ *   background(200);
+ *   text(key, 33,65); // Display last key pressed.
+ * }
+ * </div></code>
+ */
+p5.prototype.key = '';
+
+/**
+ * The variable keyCode is used to detect special keys such as BACKSPACE,
+ * DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL, OPTION, ALT, UP_ARROW,
+ * DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW.
+ *
+ * @property keyCode
+ * @example
+ * <div><code>
+ * var fillVal = 126;
+ * function draw() {
+ *   fill(fillVal);
+ *   rect(25, 25, 50, 50);
+ * }
+ *
+ * function keyPressed() {
+ *   if (keyCode == UP_ARROW) {
+ *     fillVal = 255;
+ *   } else if (keyCode == DOWN_ARROW) {
+ *     fillVal = 0;
+ *   }
+ *   return false; // prevent default
+ * }
+ * </code></div>
+ */
+p5.prototype.keyCode = 0;
+
+/**
+ * The keyPressed() function is called once every time a key is pressed. The
+ * keyCode for the key that was pressed is stored in the keyCode variable.
+ * <br><br>
+ * For non-ASCII keys, use the keyCode variable. You can check if the keyCode
+ * equals BACKSPACE, DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL,
+ * OPTION, ALT, UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW.
+ * <br><br>
+ * For ASCII keys that was pressed is stored in the key variable. However, it
+ * does not distinguish between uppercase and lowercase. For this reason, it
+ * is recommended to use keyTyped() to read the key variable, in which the
+ * case of the variable will be distinguished.
+ * <br><br>
+ * Because of how operating systems handle key repeats, holding down a key
+ * may cause multiple calls to keyTyped() (and keyReleased() as well). The
+ * rate of repeat is set by the operating system and how each computer is
+ * configured.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various key events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method keyPressed
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function keyPressed() {
+ *   if (value === 0) {
+ *     value = 255;
+ *   } else {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function keyPressed() {
+ *   if (keyCode === LEFT_ARROW) {
+ *     value = 255;
+ *   } else if (keyCode === RIGHT_ARROW) {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ * <div class="norender">
+ * <code>
+ * function keyPressed(){
+ *   // Do something
+ *   return false; // prevent any default behaviour
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onkeydown = function (e) {
+  if (downKeys[e.which]) { // prevent multiple firings
+    return;
+  }
+  this._setProperty('isKeyPressed', true);
+  this._setProperty('keyIsPressed', true);
+  this._setProperty('keyCode', e.which);
+  downKeys[e.which] = true;
+  var key = String.fromCharCode(e.which);
+  if (!key) {
+    key = e.which;
+  }
+  this._setProperty('key', key);
+  var keyPressed = this.keyPressed || window.keyPressed;
+  if (typeof keyPressed === 'function' && !e.charCode) {
+    var executeDefault = keyPressed(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+/**
+ * The keyReleased() function is called once every time a key is released.
+ * See key and keyCode for more information.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various key events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method keyReleased
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function keyReleased() {
+ *   if (value === 0) {
+ *     value = 255;
+ *   } else {
+ *     value = 0;
+ *   }
+ *   return false; // prevent any default behavior
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onkeyup = function (e) {
+  var keyReleased = this.keyReleased || window.keyReleased;
+  this._setProperty('isKeyPressed', false);
+  this._setProperty('keyIsPressed', false);
+  this._setProperty('_lastKeyCodeTyped', null);
+  downKeys[e.which] = false;
+  //delete this._downKeys[e.which];
+  var key = String.fromCharCode(e.which);
+  if (!key) {
+    key = e.which;
+  }
+  this._setProperty('key', key);
+  this._setProperty('keyCode', e.which);
+  if (typeof keyReleased === 'function') {
+    var executeDefault = keyReleased(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+
+/**
+ * The keyTyped() function is called once every time a key is pressed, but
+ * action keys such as Ctrl, Shift, and Alt are ignored. The most recent
+ * key pressed will be stored in the key variable.
+ * <br><br>
+ * Because of how operating systems handle key repeats, holding down a key
+ * will cause multiple calls to keyTyped() (and keyReleased() as well). The
+ * rate of repeat is set by the operating system and how each computer is
+ * configured.<br><br>
+ * Browsers may have different default behaviors attached to various key
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method keyTyped
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function keyTyped() {
+ *   if (key === 'a') {
+ *     value = 255;
+ *   } else if (key === 'b') {
+ *     value = 0;
+ *   }
+ *   return false; // prevent any default behavior
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onkeypress = function (e) {
+  if (e.which === this._lastKeyCodeTyped) { // prevent multiple firings
+    return;
+  }
+  this._setProperty('keyCode', e.which);
+  this._setProperty('_lastKeyCodeTyped', e.which); // track last keyCode
+  this._setProperty('key', String.fromCharCode(e.which));
+  var keyTyped = this.keyTyped || window.keyTyped;
+  if (typeof keyTyped === 'function') {
+    var executeDefault = keyTyped(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+/**
+ * The onblur function is called when the user is no longer focused
+ * on the p5 element. Because the keyup events will not fire if the user is
+ * not focused on the element we must assume all keys currently down have
+ * been released.
+ */
+p5.prototype._onblur = function (e) {
+  downKeys = {};
+};
+
+/**
+ * The keyIsDown() function checks if the key is currently down, i.e. pressed.
+ * It can be used if you have an object that moves, and you want several keys
+ * to be able to affect its behaviour simultaneously, such as moving a
+ * sprite diagonally. You can put in any number representing the keyCode of
+ * the key, or use any of the variable keyCode names listed
+ * <a href="http://p5js.org/reference/#p5/keyCode">here</a>.
+ *
+ * @method keyIsDown
+ * @param {Number}          code The key to check for.
+ * @return {Boolean}        whether key is down or not
+ * @example
+ * <div><code>
+ * var x = 100;
+ * var y = 100;
+ *
+ * function setup() {
+ *   createCanvas(512, 512);
+ * }
+ *
+ * function draw() {
+ *   if (keyIsDown(LEFT_ARROW))
+ *     x-=5;
+ *
+ *   if (keyIsDown(RIGHT_ARROW))
+ *     x+=5;
+ *
+ *   if (keyIsDown(UP_ARROW))
+ *     y-=5;
+ *
+ *   if (keyIsDown(DOWN_ARROW))
+ *     y+=5;
+ *
+ *   clear();
+ *   fill(255, 0, 0);
+ *   ellipse(x, y, 50, 50);
+ * }
+ * </code></div>
+ */
+p5.prototype.keyIsDown = function(code) {
+  return downKeys[code];
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],63:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Mouse
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+/*
+ * These are helper vars that store the mouseX and mouseY vals
+ * between the time that a mouse event happens and the next frame
+ * of draw. This is done to deal with the asynchronicity of event
+ * calls interacting with the draw loop. When a mouse event occurs
+ * the _nextMouseX/Y vars are updated, then on each call of draw, mouseX/Y
+ * and pmouseX/Y are updated using the _nextMouseX/Y vals.
+ */
+p5.prototype._nextMouseX = 0;
+p5.prototype._nextMouseY = 0;
+
+/**
+ * The system variable mouseX always contains the current horizontal
+ * position of the mouse, relative to (0, 0) of the canvas.
+ *
+ * @property mouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the canvas
+ * function draw() {
+ *   background(244, 248, 252);
+ *   line(mouseX, 0, mouseX, 100);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.mouseX = 0;
+
+/**
+ * The system variable mouseY always contains the current vertical position
+ * of the mouse, relative to (0, 0) of the canvas.
+ *
+ * @property mouseY
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the canvas
+ * function draw() {
+ *   background(244, 248, 252);
+ *   line(0, mouseY, 100, mouseY);
+ *}
+ * </code>
+ * </div>
+ */
+p5.prototype.mouseY = 0;
+
+/**
+ * The system variable pmouseX always contains the horizontal position of
+ * the mouse in the frame previous to the current frame, relative to (0, 0)
+ * of the canvas.
+ *
+ * @property pmouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the canvas to leave a trail
+ * function setup() {
+ *   //slow down the frameRate to make it more visible
+ *   frameRate(10);
+ * }
+ *
+ * function draw() {
+ *   background(244, 248, 252);
+ *   line(mouseX, mouseY, pmouseX, pmouseY);
+ *   print(pmouseX + " -> " + mouseX);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pmouseX = 0;
+
+/**
+ * The system variable pmouseY always contains the vertical position of the
+ * mouse in the frame previous to the current frame, relative to (0, 0) of
+ * the canvas.
+ *
+ * @property pmouseY
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ *   background(237, 34, 93);
+ *   fill(0);
+ *   //draw a square only if the mouse is not moving
+ *   if(mouseY == pmouseY && mouseX == pmouseX)
+ *     rect(20,20,60,60);
+ *
+ *   print(pmouseY + " -> " + mouseY);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pmouseY = 0;
+
+/**
+ * The system variable winMouseX always contains the current horizontal
+ * position of the mouse, relative to (0, 0) of the window.
+ *
+ * @property winMouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ * var myCanvas;
+ *
+ * function setup() {
+ *   //use a variable to store a pointer to the canvas
+ *   myCanvas = createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ *   background(237, 34, 93);
+ *   fill(0);
+ *
+ *   //move the canvas to the horizontal mouse position
+ *   //relative to the window
+ *   myCanvas.position(winMouseX+1, windowHeight/2);
+ *
+ *  //the y of the square is relative to the canvas
+ *  rect(20,mouseY,60,60);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.winMouseX = 0;
+
+/**
+ * The system variable winMouseY always contains the current vertical
+ * position of the mouse, relative to (0, 0) of the window.
+ *
+ * @property winMouseY
+ *
+ * @example
+ * <div>
+ * <code>
+ *var myCanvas;
+ *
+ * function setup() {
+ *   //use a variable to store a pointer to the canvas
+ *   myCanvas = createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ *   background(237, 34, 93);
+ *   fill(0);
+ *
+ *   //move the canvas to the vertical mouse position
+ *   //relative to the window
+ *   myCanvas.position(windowWidth/2, winMouseY+1);
+ *
+ *  //the x of the square is relative to the canvas
+ *  rect(mouseX,20,60,60);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.winMouseY = 0;
+
+/**
+ * The system variable pwinMouseX always contains the horizontal position
+ * of the mouse in the frame previous to the current frame, relative to
+ * (0, 0) of the window.
+ *
+ * @property pwinMouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ *
+ * var myCanvas;
+ *
+ * function setup() {
+ *   //use a variable to store a pointer to the canvas
+ *   myCanvas = createCanvas(100, 100);
+ *   noStroke();
+ *   fill(237, 34, 93);
+ *   }
+ *
+ * function draw() {
+ *   clear();
+ *   //the difference between previous and
+ *   //current x position is the horizontal mouse speed
+ *   var speed = abs(winMouseX-pwinMouseX);
+ *   //change the size of the circle
+ *   //according to the horizontal speed
+ *   ellipse(50, 50, 10+speed*5, 10+speed*5);
+ *   //move the canvas to the mouse position
+ *   myCanvas.position( winMouseX+1, winMouseY+1);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pwinMouseX = 0;
+
+/**
+ * The system variable pwinMouseY always contains the vertical position of
+ * the mouse in the frame previous to the current frame, relative to (0, 0)
+ * of the window.
+ *
+ * @property pwinMouseY
+ *
+ *
+ * @example
+ * <div>
+ * <code>
+ *
+ * var myCanvas;
+ *
+ * function setup() {
+ *   //use a variable to store a pointer to the canvas
+ *   myCanvas = createCanvas(100, 100);
+ *   noStroke();
+ *   fill(237, 34, 93);
+ *   }
+ *
+ * function draw() {
+ *   clear();
+ *   //the difference between previous and
+ *   //current y position is the vertical mouse speed
+ *   var speed = abs(winMouseY-pwinMouseY);
+ *   //change the size of the circle
+ *   //according to the vertical speed
+ *   ellipse(50, 50, 10+speed*5, 10+speed*5);
+ *   //move the canvas to the mouse position
+ *   myCanvas.position( winMouseX+1, winMouseY+1);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pwinMouseY = 0;
+
+/**
+ * Processing automatically tracks if the mouse button is pressed and which
+ * button is pressed. The value of the system variable mouseButton is either
+ * LEFT, RIGHT, or CENTER depending on which button was pressed last.
+ * Warning: different browsers may track mouseButton differently.
+ *
+ * @property mouseButton
+ *
+ * @example
+	* <div>
+	* <code>
+	* function draw() {
+	*   background(237, 34, 93);
+	*   fill(0);
+	*
+	*   if (mouseIsPressed) {
+	*     if (mouseButton == LEFT)
+	*       ellipse(50, 50, 50, 50);
+	*     if (mouseButton == RIGHT)
+	*       rect(25, 25, 50, 50);
+	*     if (mouseButton == CENTER)
+	*       triangle(23, 75, 50, 20, 78, 75);
+	*   }
+	*
+	*   print(mouseButton);
+	* }
+	* </code>
+ * </div>
+ */
+p5.prototype.mouseButton = 0;
+
+/**
+ * The boolean system variable mouseIsPressed is true if the mouse is pressed
+ * and false if not.
+ *
+ * @property mouseIsPressed
+ *
+ * @example
+	* <div>
+	* <code>
+	* function draw() {
+	*   background(237, 34, 93);
+	*   fill(0);
+	*
+	*   if (mouseIsPressed)
+	*     ellipse(50, 50, 50, 50);
+	*   else
+	*     rect(25, 25, 50, 50);
+	*
+	*   print(mouseIsPressed);
+	* }
+	* </code>
+	* </div>
+ */
+p5.prototype.mouseIsPressed = false;
+p5.prototype.isMousePressed = false; // both are supported
+
+p5.prototype._updateNextMouseCoords = function(e) {
+  if(e.type === 'touchstart' ||
+     e.type === 'touchmove' ||
+     e.type === 'touchend') {
+    this._setProperty('_nextMouseX', this._nextTouchX);
+    this._setProperty('_nextMouseY', this._nextTouchY);
+  } else {
+    if(this._curElement !== null) {
+      var mousePos = getMousePos(this._curElement.elt, e);
+      this._setProperty('_nextMouseX', mousePos.x);
+      this._setProperty('_nextMouseY', mousePos.y);
+    }
+  }
+  this._setProperty('winMouseX', e.pageX);
+  this._setProperty('winMouseY', e.pageY);
+};
+
+p5.prototype._updateMouseCoords = function() {
+  this._setProperty('pmouseX', this.mouseX);
+  this._setProperty('pmouseY', this.mouseY);
+  this._setProperty('mouseX', this._nextMouseX);
+  this._setProperty('mouseY', this._nextMouseY);
+  this._setProperty('pwinMouseX', this.winMouseX);
+  this._setProperty('pwinMouseY', this.winMouseY);
+};
+
+function getMousePos(canvas, evt) {
+  var rect = canvas.getBoundingClientRect();
+  return {
+    x: evt.clientX - rect.left,
+    y: evt.clientY - rect.top
+  };
+}
+
+p5.prototype._setMouseButton = function(e) {
+  if (e.button === 1) {
+    this._setProperty('mouseButton', constants.CENTER);
+  } else if (e.button === 2) {
+    this._setProperty('mouseButton', constants.RIGHT);
+  } else {
+    this._setProperty('mouseButton', constants.LEFT);
+  }
+};
+
+/**
+ * The mouseMoved() function is called every time the mouse moves and a mouse
+ * button is not pressed.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseMoved
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function mouseMoved() {
+ *   value = value + 5;
+ *   if (value > 255) {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseMoved() {
+ *   ellipse(mouseX, mouseY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+
+/**
+ * The mouseDragged() function is called once every time the mouse moves and
+ * a mouse button is pressed. If no mouseDragged() function is defined, the
+ * touchMoved() function will be called instead if it is defined.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseDragged
+ * @example
+ * <div>
+ * <code>
+ * // Drag the mouse across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function mouseDragged() {
+ *   value = value + 5;
+ *   if (value > 255) {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseDragged() {
+ *   ellipse(mouseX, mouseY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onmousemove = function(e){
+  var context = this._isGlobal ? window : this;
+  var executeDefault;
+  this._updateNextMouseCoords(e);
+  this._updateNextTouchCoords(e);
+  if (!this.isMousePressed) {
+    if (typeof context.mouseMoved === 'function') {
+      executeDefault = context.mouseMoved(e);
+      if(executeDefault === false) {
+        e.preventDefault();
+      }
+    }
+  }
+  else {
+    if (typeof context.mouseDragged === 'function') {
+      executeDefault = context.mouseDragged(e);
+      if(executeDefault === false) {
+        e.preventDefault();
+      }
+    } else if (typeof context.touchMoved === 'function') {
+      executeDefault = context.touchMoved(e);
+      if(executeDefault === false) {
+        e.preventDefault();
+      }
+    }
+  }
+};
+
+/**
+ * The mousePressed() function is called once after every time a mouse button
+ * is pressed. The mouseButton variable (see the related reference entry)
+ * can be used to determine which button has been pressed. If no
+ * mousePressed() function is defined, the touchStarted() function will be
+ * called instead if it is defined.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mousePressed
+ * @example
+ * <div>
+ * <code>
+ * // Click within the image to change
+ * // the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function mousePressed() {
+ *   if (value == 0) {
+ *     value = 255;
+ *   } else {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mousePressed() {
+ *   ellipse(mouseX, mouseY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onmousedown = function(e) {
+  var context = this._isGlobal ? window : this;
+  var executeDefault;
+  this._setProperty('isMousePressed', true);
+  this._setProperty('mouseIsPressed', true);
+  this._setMouseButton(e);
+  this._updateNextMouseCoords(e);
+  this._updateNextTouchCoords(e);
+  if (typeof context.mousePressed === 'function') {
+    executeDefault = context.mousePressed(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  } else if (typeof context.touchStarted === 'function') {
+    executeDefault = context.touchStarted(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+
+/**
+ * The mouseReleased() function is called every time a mouse button is
+ * released. If no mouseReleased() function is defined, the touchEnded()
+ * function will be called instead if it is defined.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ *
+ * @method mouseReleased
+ * @example
+ * <div>
+ * <code>
+ * // Click within the image to change
+ * // the value of the rectangle
+ * // after the mouse has been clicked
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function mouseReleased() {
+ *   if (value == 0) {
+ *     value = 255;
+ *   } else {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseReleased() {
+ *   ellipse(mouseX, mouseY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onmouseup = function(e) {
+  var context = this._isGlobal ? window : this;
+  var executeDefault;
+  this._setProperty('isMousePressed', false);
+  this._setProperty('mouseIsPressed', false);
+  if (typeof context.mouseReleased === 'function') {
+    executeDefault = context.mouseReleased(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  } else if (typeof context.touchEnded === 'function') {
+    executeDefault = context.touchEnded(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+
+p5.prototype._ondragend = p5.prototype._onmouseup;
+p5.prototype._ondragover = p5.prototype._onmousemove;
+
+/**
+ * The mouseClicked() function is called once after a mouse button has been
+ * pressed and then released.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseClicked
+ * @example
+ * <div>
+ * <code>
+ * // Click within the image to change
+ * // the value of the rectangle
+ * // after the mouse has been clicked
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function mouseClicked() {
+ *   if (value == 0) {
+ *     value = 255;
+ *   } else {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseClicked() {
+ *   ellipse(mouseX, mouseY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onclick = function(e) {
+  var context = this._isGlobal ? window : this;
+  if (typeof context.mouseClicked === 'function') {
+    var executeDefault = context.mouseClicked(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+
+/**
+ * The function mouseWheel() is executed every time a vertical mouse wheel
+ * event is detected either triggered by an actual mouse wheel or by a
+ * touchpad.<br><br>
+ * The event.delta property returns the amount the mouse wheel
+ * have scrolled. The values can be positive or negative depending on the
+ * scroll direction (on OS X with "natural" scrolling enabled, the signs
+ * are inverted).<br><br>
+ * Browsers may have different default behaviors attached to various
+ * mouse events. To prevent any default behavior for this event, add
+ * "return false" to the end of the method.<br><br>
+ * Due to the current support of the "wheel" event on Safari, the function
+ * may only work as expected if "return false" is included while using Safari.
+ *
+ * @method mouseWheel
+ *
+ * @example
+ * <div>
+ * <code>
+ * var pos = 25;
+ *
+ * function draw() {
+ *   background(237, 34, 93);
+ *   fill(0);
+ *   rect(25, pos, 50, 50);
+ * }
+ *
+ * function mouseWheel(event) {
+ *   print(event.delta);
+ *   //move the square according to the vertical scroll amount
+ *   pos += event.delta;
+ *   //uncomment to block page scrolling
+ *   //return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onwheel = function(e) {
+  var context = this._isGlobal ? window : this;
+  if (typeof context.mouseWheel === 'function') {
+    e.delta = e.deltaY;
+    var executeDefault = context.mouseWheel(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48}],64:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Touch
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/*
+ * These are helper vars that store the touchX and touchY vals
+ * between the time that a mouse event happens and the next frame
+ * of draw. This is done to deal with the asynchronicity of event
+ * calls interacting with the draw loop. When a touch event occurs
+ * the _nextTouchX/Y vars are updated, then on each call of draw, touchX/Y
+ * and ptouchX/Y are updated using the _nextMouseX/Y vals.
+ */
+p5.prototype._nextTouchX = 0;
+p5.prototype._nextTouchY = 0;
+
+/**
+ * The system variable touchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the canvas. This is best used for
+ * single touch interactions. For multi-touch interactions, use the
+ * touches[] array.
+ *
+ * @property touchX
+ */
+p5.prototype.touchX = 0;
+
+/**
+ * The system variable touchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the canvas. This is best used for
+ * single touch interactions. For multi-touch interactions, use the
+ * touches[] array.
+ *
+ * @property touchY
+ */
+p5.prototype.touchY = 0;
+
+/**
+ * The system variable ptouchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the canvas, in the frame previous to the
+ * current frame.
+ *
+ * @property ptouchX
+ */
+p5.prototype.ptouchX = 0;
+
+/**
+ * The system variable ptouchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the canvas, in the frame previous to the
+ * current frame.
+ *
+ * @property ptouchY
+ */
+p5.prototype.ptouchY = 0;
+
+/**
+ * The system variable touches[] contains an array of the positions of all
+ * current touch points, relative to (0, 0) of the canvas, and IDs identifying a
+ * unique touch as it moves. Each element in the array is an object with x, y,
+ * and id properties.
+ *
+ * @property touches[]
+ */
+p5.prototype.touches = [];
+
+/**
+ * The boolean system variable touchIsDown is true if the screen is
+ * touched and false if not.
+ *
+ * @property touchIsDown
+ */
+p5.prototype.touchIsDown = false;
+
+p5.prototype._updateNextTouchCoords = function(e) {
+  if(e.type === 'mousedown' ||
+     e.type === 'mousemove' ||
+     e.type === 'mouseup'){
+    this._setProperty('_nextTouchX', this._nextMouseX);
+    this._setProperty('_nextTouchY', this._nextMouseY);
+  } else {
+    if(this._curElement !== null) {
+      var touchInfo = getTouchInfo(this._curElement.elt, e, 0);
+      this._setProperty('_nextTouchX', touchInfo.x);
+      this._setProperty('_nextTouchY', touchInfo.y);
+
+      var touches = [];
+      for(var i = 0; i < e.touches.length; i++){
+        touches[i] = getTouchInfo(this._curElement.elt, e, i);
+      }
+      this._setProperty('touches', touches);
+    }
+  }
+};
+
+p5.prototype._updateTouchCoords = function() {
+  this._setProperty('ptouchX', this.touchX);
+  this._setProperty('ptouchY', this.touchY);
+  this._setProperty('touchX', this._nextTouchX);
+  this._setProperty('touchY', this._nextTouchY);
+};
+
+function getTouchInfo(canvas, e, i) {
+  i = i || 0;
+  var rect = canvas.getBoundingClientRect();
+  var touch = e.touches[i] || e.changedTouches[i];
+  return {
+    x: touch.clientX - rect.left,
+    y: touch.clientY - rect.top,
+    id: touch.identifier
+  };
+}
+
+/**
+ * The touchStarted() function is called once after every time a touch is
+ * registered. If no touchStarted() function is defined, the mousePressed()
+ * function will be called instead if it is defined.<br><br>
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchStarted
+ * @example
+ * <div>
+ * <code>
+ * // Touch within the image to change
+ * // the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function touchStarted() {
+ *   if (value == 0) {
+ *     value = 255;
+ *   } else {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function touchStarted() {
+ *   ellipse(touchX, touchY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._ontouchstart = function(e) {
+  var context = this._isGlobal ? window : this;
+  var executeDefault;
+  this._updateNextTouchCoords(e);
+  this._updateNextMouseCoords(e);
+  this._setProperty('touchIsDown', true);
+  if(typeof context.touchStarted === 'function') {
+    executeDefault = context.touchStarted(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  } else if (typeof context.mousePressed === 'function') {
+    executeDefault = context.mousePressed(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+    //this._setMouseButton(e);
+  }
+};
+
+/**
+ * The touchMoved() function is called every time a touch move is registered.
+ * If no touchMoved() function is defined, the mouseDragged() function will
+ * be called instead if it is defined.<br><br>
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchMoved
+ * @example
+ * <div>
+ * <code>
+ * // Move your finger across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function touchMoved() {
+ *   value = value + 5;
+ *   if (value > 255) {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function touchMoved() {
+ *   ellipse(touchX, touchY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._ontouchmove = function(e) {
+  var context = this._isGlobal ? window : this;
+  var executeDefault;
+  this._updateNextTouchCoords(e);
+  this._updateNextMouseCoords(e);
+  if (typeof context.touchMoved === 'function') {
+    executeDefault = context.touchMoved(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  } else if (typeof context.mouseDragged === 'function') {
+    executeDefault = context.mouseDragged(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+
+/**
+ * The touchEnded() function is called every time a touch ends. If no
+ * touchEnded() function is defined, the mouseReleased() function will be
+ * called instead if it is defined.<br><br>
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchEnded
+ * @example
+ * <div>
+ * <code>
+ * // Release touch within the image to
+ * // change the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ *   fill(value);
+ *   rect(25, 25, 50, 50);
+ * }
+ * function touchEnded() {
+ *   if (value == 0) {
+ *     value = 255;
+ *   } else {
+ *     value = 0;
+ *   }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function touchEnded() {
+ *   ellipse(touchX, touchY, 5, 5);
+ *   // prevent default
+ *   return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._ontouchend = function(e) {
+  this._updateNextTouchCoords(e);
+  this._updateNextMouseCoords(e);
+  if (this.touches.length === 0) {
+    this._setProperty('touchIsDown', false);
+  }
+  var context = this._isGlobal ? window : this;
+  var executeDefault;
+  if (typeof context.touchEnded === 'function') {
+    executeDefault = context.touchEnded(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  } else if (typeof context.mouseReleased === 'function') {
+    executeDefault = context.mouseReleased(e);
+    if(executeDefault === false) {
+      e.preventDefault();
+    }
+  }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],65:[function(_dereq_,module,exports){
+/*global ImageData:false */
+
+/**
+ * This module defines the filters for use with image buffers.
+ *
+ * This module is basically a collection of functions stored in an object
+ * as opposed to modules. The functions are destructive, modifying
+ * the passed in canvas rather than creating a copy.
+ *
+ * Generally speaking users of this module will use the Filters.apply method
+ * on a canvas to create an effect.
+ *
+ * A number of functions are borrowed/adapted from
+ * http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
+ * or the java processing implementation.
+ */
+
+'use strict';
+
+var Filters = {};
+
+
+/*
+ * Helper functions
+ */
+
+
+/**
+ * Returns the pixel buffer for a canvas
+ *
+ * @private
+ *
+ * @param  {Canvas|ImageData} canvas the canvas to get pixels from
+ * @return {Uint8ClampedArray}       a one-dimensional array containing
+ *                                   the data in thc RGBA order, with integer
+ *                                   values between 0 and 255
+ */
+Filters._toPixels = function (canvas) {
+  if (canvas instanceof ImageData) {
+    return canvas.data;
+  } else {
+    return canvas.getContext('2d').getImageData(
+      0,
+      0,
+      canvas.width,
+      canvas.height
+    ).data;
+  }
+};
+
+/**
+ * Returns a 32 bit number containing ARGB data at ith pixel in the
+ * 1D array containing pixels data.
+ *
+ * @private
+ *
+ * @param  {Uint8ClampedArray} data array returned by _toPixels()
+ * @param  {Integer}           i    index of a 1D Image Array
+ * @return {Integer}                32 bit integer value representing
+ *                                  ARGB value.
+ */
+Filters._getARGB = function (data, i) {
+  var offset = i * 4;
+  return (data[offset+3] << 24) & 0xff000000 |
+    (data[offset] << 16) & 0x00ff0000 |
+    (data[offset+1] << 8) & 0x0000ff00 |
+    data[offset+2] & 0x000000ff;
+};
+
+/**
+ * Modifies pixels RGBA values to values contained in the data object.
+ *
+ * @private
+ *
+ * @param {Uint8ClampedArray} pixels array returned by _toPixels()
+ * @param {Int32Array}        data   source 1D array where each value
+ *                                   represents ARGB values
+ */
+Filters._setPixels = function (pixels, data) {
+  var offset = 0;
+  for( var i = 0, al = pixels.length; i < al; i++) {
+    offset = i*4;
+    pixels[offset + 0] = (data[i] & 0x00ff0000)>>>16;
+    pixels[offset + 1] = (data[i] & 0x0000ff00)>>>8;
+    pixels[offset + 2] = (data[i] & 0x000000ff);
+    pixels[offset + 3] = (data[i] & 0xff000000)>>>24;
+  }
+};
+
+/**
+ * Returns the ImageData object for a canvas
+ * https://developer.mozilla.org/en-US/docs/Web/API/ImageData
+ *
+ * @private
+ *
+ * @param  {Canvas|ImageData} canvas canvas to get image data from
+ * @return {ImageData}               Holder of pixel data (and width and
+ *                                   height) for a canvas
+ */
+Filters._toImageData = function (canvas) {
+  if (canvas instanceof ImageData) {
+    return canvas;
+  } else {
+    return canvas.getContext('2d').getImageData(
+      0,
+      0,
+      canvas.width,
+      canvas.height
+    );
+  }
+};
+
+/**
+ * Returns a blank ImageData object.
+ *
+ * @private
+ *
+ * @param  {Integer} width
+ * @param  {Integer} height
+ * @return {ImageData}
+ */
+Filters._createImageData = function (width, height) {
+  Filters._tmpCanvas = document.createElement('canvas');
+  Filters._tmpCtx = Filters._tmpCanvas.getContext('2d');
+  return this._tmpCtx.createImageData(width, height);
+};
+
+
+/**
+ * Applys a filter function to a canvas.
+ *
+ * The difference between this and the actual filter functions defined below
+ * is that the filter functions generally modify the pixel buffer but do
+ * not actually put that data back to the canvas (where it would actually
+ * update what is visible). By contrast this method does make the changes
+ * actually visible in the canvas.
+ *
+ * The apply method is the method that callers of this module would generally
+ * use. It has been separated from the actual filters to support an advanced
+ * use case of creating a filter chain that executes without actually updating
+ * the canvas in between everystep.
+ *
+ * @param  {[type]} func   [description]
+ * @param  {[type]} canvas [description]
+ * @param  {[type]} level  [description]
+ * @return {[type]}        [description]
+ */
+Filters.apply = function (canvas, func, filterParam) {
+  var ctx = canvas.getContext('2d');
+  var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+
+  //Filters can either return a new ImageData object, or just modify
+  //the one they received.
+  var newImageData = func(imageData, filterParam);
+  if (newImageData instanceof ImageData) {
+    ctx.putImageData(newImageData, 0, 0, 0, 0, canvas.width, canvas.height);
+  } else {
+    ctx.putImageData(imageData, 0, 0, 0, 0, canvas.width, canvas.height);
+  }
+};
+
+
+/*
+ * Filters
+ */
+
+
+/**
+ * Converts the image to black and white pixels depending if they are above or
+ * below the threshold defined by the level parameter. The parameter must be
+ * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used.
+ *
+ * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
+ *
+ * @param  {Canvas} canvas
+ * @param  {Float} level
+ */
+Filters.threshold = function (canvas, level) {
+  var pixels = Filters._toPixels(canvas);
+
+  if (level === undefined) {
+    level = 0.5;
+  }
+  var thresh = Math.floor(level * 255);
+
+  for (var i = 0; i < pixels.length; i += 4) {
+    var r = pixels[i];
+    var g = pixels[i + 1];
+    var b = pixels[i + 2];
+    var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b);
+    var val;
+    if (gray >= thresh) {
+      val = 255;
+    } else {
+      val = 0;
+    }
+    pixels[i] = pixels[i + 1] = pixels[i + 2] = val;
+  }
+
+};
+
+
+/**
+ * Converts any colors in the image to grayscale equivalents.
+ * No parameter is used.
+ *
+ * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
+ *
+ * @param {Canvas} canvas
+ */
+Filters.gray = function (canvas) {
+  var pixels = Filters._toPixels(canvas);
+
+  for (var i = 0; i < pixels.length; i += 4) {
+    var r = pixels[i];
+    var g = pixels[i + 1];
+    var b = pixels[i + 2];
+
+    // CIE luminance for RGB
+    var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b);
+    pixels[i] = pixels[i + 1] = pixels[i + 2] = gray;
+  }
+};
+
+/**
+ * Sets the alpha channel to entirely opaque. No parameter is used.
+ *
+ * @param {Canvas} canvas
+ */
+Filters.opaque = function (canvas) {
+  var pixels = Filters._toPixels(canvas);
+
+  for (var i = 0; i < pixels.length; i += 4) {
+    pixels[i + 3] = 255;
+  }
+
+  return pixels;
+};
+
+/**
+ * Sets each pixel to its inverse value. No parameter is used.
+ * @param {Invert}
+ */
+Filters.invert = function (canvas) {
+  var pixels = Filters._toPixels(canvas);
+
+  for (var i = 0; i < pixels.length; i += 4) {
+    pixels[i] = 255 - pixels[i];
+    pixels[i + 1] = 255 - pixels[i + 1];
+    pixels[i + 2] = 255 - pixels[i + 2];
+  }
+
+};
+
+
+/**
+ * Limits each channel of the image to the number of colors specified as
+ * the parameter. The parameter can be set to values between 2 and 255, but
+ * results are most noticeable in the lower ranges.
+ *
+ * Adapted from java based processing implementation
+ *
+ * @param  {Canvas} canvas
+ * @param  {Integer} level
+ */
+Filters.posterize = function (canvas, level) {
+  var pixels = Filters._toPixels(canvas);
+
+  if ((level < 2) || (level > 255)) {
+    throw new Error(
+      'Level must be greater than 2 and less than 255 for posterize'
+    );
+  }
+
+  var levels1 = level - 1;
+  for (var i = 0; i < pixels.length; i+=4) {
+    var rlevel = pixels[i];
+    var glevel = pixels[i + 1];
+    var blevel = pixels[i + 2];
+
+    pixels[i] = (((rlevel * level) >> 8) * 255) / levels1;
+    pixels[i + 1] = (((glevel * level) >> 8) * 255) / levels1;
+    pixels[i + 2] = (((blevel * level) >> 8) * 255) / levels1;
+  }
+};
+
+/**
+ * reduces the bright areas in an image
+ * @param  {Canvas} canvas
+ *
+ */
+Filters.dilate = function (canvas) {
+  var pixels = Filters._toPixels(canvas);
+  var currIdx = 0;
+  var maxIdx = pixels.length ? pixels.length/4 : 0;
+  var out = new Int32Array(maxIdx);
+  var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
+  var idxRight, idxLeft, idxUp, idxDown,
+      colRight, colLeft, colUp, colDown,
+      lumRight, lumLeft, lumUp, lumDown;
+
+  while(currIdx < maxIdx) {
+    currRowIdx = currIdx;
+    maxRowIdx = currIdx + canvas.width;
+    while (currIdx < maxRowIdx) {
+      colOrig = colOut = Filters._getARGB(pixels, currIdx);
+      idxLeft = currIdx - 1;
+      idxRight = currIdx + 1;
+      idxUp = currIdx - canvas.width;
+      idxDown = currIdx + canvas.width;
+
+      if (idxLeft < currRowIdx) {
+        idxLeft = currIdx;
+      }
+      if (idxRight >= maxRowIdx) {
+        idxRight = currIdx;
+      }
+      if (idxUp < 0){
+        idxUp = 0;
+      }
+      if (idxDown >= maxIdx) {
+        idxDown = currIdx;
+      }
+      colUp = Filters._getARGB(pixels, idxUp);
+      colLeft = Filters._getARGB(pixels, idxLeft);
+      colDown = Filters._getARGB(pixels, idxDown);
+      colRight = Filters._getARGB(pixels, idxRight);
+
+      //compute luminance
+      currLum = 77*(colOrig>>16&0xff) +
+        151*(colOrig>>8&0xff) +
+        28*(colOrig&0xff);
+      lumLeft = 77*(colLeft>>16&0xff) +
+        151*(colLeft>>8&0xff) +
+        28*(colLeft&0xff);
+      lumRight = 77*(colRight>>16&0xff) +
+        151*(colRight>>8&0xff) +
+        28*(colRight&0xff);
+      lumUp = 77*(colUp>>16&0xff) +
+        151*(colUp>>8&0xff) +
+        28*(colUp&0xff);
+      lumDown = 77*(colDown>>16&0xff) +
+        151*(colDown>>8&0xff) +
+        28*(colDown&0xff);
+
+      if (lumLeft > currLum) {
+        colOut = colLeft;
+        currLum = lumLeft;
+      }
+      if (lumRight > currLum) {
+        colOut = colRight;
+        currLum = lumRight;
+      }
+      if (lumUp > currLum) {
+        colOut = colUp;
+        currLum = lumUp;
+      }
+      if (lumDown > currLum) {
+        colOut = colDown;
+        currLum = lumDown;
+      }
+      out[currIdx++]=colOut;
+    }
+  }
+  Filters._setPixels(pixels, out);
+};
+
+/**
+ * increases the bright areas in an image
+ * @param  {Canvas} canvas
+ *
+ */
+Filters.erode = function(canvas) {
+  var pixels = Filters._toPixels(canvas);
+  var currIdx = 0;
+  var maxIdx = pixels.length ? pixels.length/4 : 0;
+  var out = new Int32Array(maxIdx);
+  var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
+  var idxRight, idxLeft, idxUp, idxDown,
+      colRight, colLeft, colUp, colDown,
+      lumRight, lumLeft, lumUp, lumDown;
+
+  while(currIdx < maxIdx) {
+    currRowIdx = currIdx;
+    maxRowIdx = currIdx + canvas.width;
+    while (currIdx < maxRowIdx) {
+      colOrig = colOut = Filters._getARGB(pixels, currIdx);
+      idxLeft = currIdx - 1;
+      idxRight = currIdx + 1;
+      idxUp = currIdx - canvas.width;
+      idxDown = currIdx + canvas.width;
+
+      if (idxLeft < currRowIdx) {
+        idxLeft = currIdx;
+      }
+      if (idxRight >= maxRowIdx) {
+        idxRight = currIdx;
+      }
+      if (idxUp < 0) {
+        idxUp = 0;
+      }
+      if (idxDown >= maxIdx) {
+        idxDown = currIdx;
+      }
+      colUp = Filters._getARGB(pixels, idxUp);
+      colLeft = Filters._getARGB(pixels, idxLeft);
+      colDown = Filters._getARGB(pixels, idxDown);
+      colRight = Filters._getARGB(pixels, idxRight);
+
+      //compute luminance
+      currLum = 77*(colOrig>>16&0xff) +
+        151*(colOrig>>8&0xff) +
+        28*(colOrig&0xff);
+      lumLeft = 77*(colLeft>>16&0xff) +
+        151*(colLeft>>8&0xff) +
+        28*(colLeft&0xff);
+      lumRight = 77*(colRight>>16&0xff) +
+        151*(colRight>>8&0xff) +
+        28*(colRight&0xff);
+      lumUp = 77*(colUp>>16&0xff) +
+        151*(colUp>>8&0xff) +
+        28*(colUp&0xff);
+      lumDown = 77*(colDown>>16&0xff) +
+        151*(colDown>>8&0xff) +
+        28*(colDown&0xff);
+
+      if (lumLeft < currLum) {
+        colOut = colLeft;
+        currLum = lumLeft;
+      }
+      if (lumRight < currLum) {
+        colOut = colRight;
+        currLum = lumRight;
+      }
+      if (lumUp < currLum) {
+        colOut = colUp;
+        currLum = lumUp;
+      }
+      if (lumDown < currLum) {
+        colOut = colDown;
+        currLum = lumDown;
+      }
+
+      out[currIdx++]=colOut;
+    }
+  }
+  Filters._setPixels(pixels, out);
+};
+
+// BLUR
+
+// internal kernel stuff for the gaussian blur filter
+var blurRadius;
+var blurKernelSize;
+var blurKernel;
+var blurMult;
+
+/*
+ * Port of https://github.com/processing/processing/blob/
+ * master/core/src/processing/core/PImage.java#L1250
+ *
+ * Optimized code for building the blur kernel.
+ * further optimized blur code (approx. 15% for radius=20)
+ * bigger speed gains for larger radii (~30%)
+ * added support for various image types (ALPHA, RGB, ARGB)
+ * [toxi 050728]
+ */
+function buildBlurKernel(r) {
+  var radius = (r * 3.5)|0;
+  radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
+
+  if (blurRadius !== radius) {
+    blurRadius = radius;
+    blurKernelSize = 1 + blurRadius<<1;
+    blurKernel = new Int32Array(blurKernelSize);
+    blurMult = new Array(blurKernelSize);
+    for(var l = 0; l < blurKernelSize; l++){
+      blurMult[l] = new Int32Array(256);
+    }
+
+    var bk,bki;
+    var bm,bmi;
+
+    for (var i = 1, radiusi = radius - 1; i < radius; i++) {
+      blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi;
+      bm = blurMult[radius+i];
+      bmi = blurMult[radiusi--];
+      for (var j = 0; j < 256; j++){
+        bm[j] = bmi[j] = bki * j;
+      }
+    }
+    bk = blurKernel[radius] = radius * radius;
+    bm = blurMult[radius];
+
+    for (var k = 0; k < 256; k++){
+      bm[k] = bk * k;
+    }
+  }
+
+}
+
+// Port of https://github.com/processing/processing/blob/
+// master/core/src/processing/core/PImage.java#L1433
+function blurARGB(canvas, radius) {
+  var pixels = Filters._toPixels(canvas);
+  var width = canvas.width;
+  var height = canvas.height;
+  var numPackedPixels = width * height;
+  var argb = new Int32Array(numPackedPixels);
+  for (var j = 0; j < numPackedPixels; j++) {
+    argb[j] = Filters._getARGB(pixels, j);
+  }
+  var sum, cr, cg, cb, ca;
+  var read, ri, ym, ymi, bk0;
+  var a2 = new Int32Array(numPackedPixels);
+  var r2 = new Int32Array(numPackedPixels);
+  var g2 = new Int32Array(numPackedPixels);
+  var b2 = new Int32Array(numPackedPixels);
+  var yi = 0;
+  buildBlurKernel(radius);
+  var x, y, i;
+  var bm;
+  for (y = 0; y < height; y++) {
+    for (x = 0; x < width; x++) {
+      cb = cg = cr = ca = sum = 0;
+      read = x - blurRadius;
+      if (read < 0) {
+        bk0 = -read;
+        read = 0;
+      } else {
+        if (read >= width) {
+          break;
+        }
+        bk0 = 0;
+      }
+      for (i = bk0; i < blurKernelSize; i++) {
+        if (read >= width) {
+          break;
+        }
+        var c = argb[read + yi];
+        bm = blurMult[i];
+        ca += bm[(c & -16777216) >>> 24];
+        cr += bm[(c & 16711680) >> 16];
+        cg += bm[(c & 65280) >> 8];
+        cb += bm[c & 255];
+        sum += blurKernel[i];
+        read++;
+      }
+      ri = yi + x;
+      a2[ri] = ca / sum;
+      r2[ri] = cr / sum;
+      g2[ri] = cg / sum;
+      b2[ri] = cb / sum;
+    }
+    yi += width;
+  }
+  yi = 0;
+  ym = -blurRadius;
+  ymi = ym * width;
+  for (y = 0; y < height; y++) {
+    for (x = 0; x < width; x++) {
+      cb = cg = cr = ca = sum = 0;
+      if (ym < 0) {
+        bk0 = ri = -ym;
+        read = x;
+      } else {
+        if (ym >= height) {
+          break;
+        }
+        bk0 = 0;
+        ri = ym;
+        read = x + ymi;
+      }
+      for (i = bk0; i < blurKernelSize; i++) {
+        if (ri >= height) {
+          break;
+        }
+        bm = blurMult[i];
+        ca += bm[a2[read]];
+        cr += bm[r2[read]];
+        cg += bm[g2[read]];
+        cb += bm[b2[read]];
+        sum += blurKernel[i];
+        ri++;
+        read += width;
+      }
+      argb[x + yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum);
+    }
+    yi += width;
+    ymi += width;
+    ym++;
+  }
+  Filters._setPixels(pixels, argb);
+}
+
+Filters.blur = function(canvas, radius){
+  blurARGB(canvas, radius);
+};
+
+
+module.exports = Filters;
+
+},{}],66:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Image
+ * @for p5
+ * @requires core
+ */
+
+/**
+ * This module defines the p5 methods for the p5.Image class
+ * for drawing images to the main display canvas.
+ */
+'use strict';
+
+
+var p5 = _dereq_('../core/core');
+
+/* global frames:true */// This is not global, but JSHint is not aware that
+// this module is implicitly enclosed with Browserify: this overrides the
+// redefined-global error and permits using the name "frames" for the array
+// of saved animation frames.
+var frames = [];
+
+
+/**
+ * Creates a new p5.Image (the datatype for storing images). This provides a
+ * fresh buffer of pixels to play with. Set the size of the buffer with the
+ * width and height parameters.
+ * <br><br>
+ * .pixels gives access to an array containing the values for all the pixels
+ * in the display window.
+ * These values are numbers. This array is the size (including an appropriate
+ * factor for the pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. See .pixels for
+ * more info. It may also be simpler to use set() or get().
+ * <br><br>
+ * Before accessing the pixels of an image, the data must loaded with the
+ * loadPixels() function. After the array data has been modified, the
+ * updatePixels() function must be run to update the changes.
+ *
+ * @method createImage
+ * @param  {Integer} width  width in pixels
+ * @param  {Integer} height height in pixels
+ * @return {p5.Image}       the p5.Image object
+ * @example
+ * <div>
+ * <code>
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ *   for (j = 0; j < img.height; j++) {
+ *     img.set(i, j, color(0, 90, 102));
+ *   }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ *   for (j = 0; j < img.height; j++) {
+ *     img.set(i, j, color(0, 90, 102, i % img.width * 2));
+ *   }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * image(img, 34, 34);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var pink = color(255, 102, 204);
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * var d = pixelDensity;
+ * var halfImage = 4 * (width * d) * (height/2 * d);
+ * for (var i = 0; i < halfImage; i+=4) {
+ *   img.pixels[i] = red(pink);
+ *   img.pixels[i+1] = green(pink);
+ *   img.pixels[i+2] = blue(pink);
+ *   img.pixels[i+3] = alpha(pink);
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * </code>
+ * </div>
+ */
+p5.prototype.createImage = function(width, height) {
+  return new p5.Image(width, height);
+};
+
+/**
+ *  Save the current canvas as an image. In Safari, this will open the
+ *  image in the window and the user must provide their own
+ *  filename on save-as. Other browsers will either save the
+ *  file immediately, or prompt the user with a dialogue window.
+ *
+ *  @method saveCanvas
+ *  @param  {[selectedCanvas]} canvas a variable representing a
+ *                             specific html5 canvas (optional)
+ *  @param  {[String]} filename
+ *  @param  {[String]} extension 'jpg' or 'png'
+ *  @example
+ *  <div class='norender'><code>
+ *  function setup() {
+ *    var c = createCanvas(100, 100);
+ *    background(255, 0, 0);
+ *    saveCanvas(c, 'myCanvas', 'jpg');
+ *  }
+ *  </code></div>
+ *  <div class='norender'><code>
+ *  // note that this example has the same result as above
+ *  // if no canvas is specified, defaults to main canvas
+ *  function setup() {
+ *    createCanvas(100, 100);
+ *    background(255, 0, 0);
+ *    saveCanvas('myCanvas', 'jpg');
+ *  }
+ *  </code></div>
+ *  <div class='norender'><code>
+ *  // all of the following are valid
+ *  saveCanvas(c, 'myCanvas', 'jpg');
+ *  saveCanvas(c, 'myCanvas');
+ *  saveCanvas(c);
+ *  saveCanvas('myCanvas', 'png');
+ *  saveCanvas('myCanvas');
+ *  saveCanvas();
+ *  </code></div>
+ */
+p5.prototype.saveCanvas = function() {
+
+  var cnv, filename, extension;
+  if (arguments.length === 3) {
+    cnv = arguments[0];
+    filename = arguments[1];
+    extension = arguments[2];
+  } else if (arguments.length === 2) {
+    if (typeof arguments[0] === 'object') {
+      cnv = arguments[0];
+      filename = arguments[1];
+    } else {
+      filename = arguments[0];
+      extension = arguments[1];
+    }
+  } else if (arguments.length === 1) {
+    if (typeof arguments[0] === 'object') {
+      cnv = arguments[0];
+    } else {
+      filename = arguments[0];
+    }
+  }
+
+  if (cnv instanceof p5.Element) {
+    cnv = cnv.elt;
+  }
+  if (!(cnv instanceof HTMLCanvasElement)) {
+    cnv = null;
+  }
+
+  if (!extension) {
+    extension = p5.prototype._checkFileExtension(filename, extension)[1];
+    if (extension === '') {
+      extension = 'png';
+    }
+  }
+
+  if (!cnv) {
+    if (this._curElement && this._curElement.elt) {
+      cnv = this._curElement.elt;
+    }
+  }
+
+  if ( p5.prototype._isSafari() ) {
+    var aText = 'Hello, Safari user!\n';
+    aText += 'Now capturing a screenshot...\n';
+    aText += 'To save this image,\n';
+    aText += 'go to File --> Save As.\n';
+    alert(aText);
+    window.location.href = cnv.toDataURL();
+  } else {
+    var mimeType;
+    if (typeof(extension) === 'undefined') {
+      extension = 'png';
+      mimeType = 'image/png';
+    }
+    else {
+      switch(extension){
+      case 'png':
+        mimeType = 'image/png';
+        break;
+      case 'jpeg':
+        mimeType = 'image/jpeg';
+        break;
+      case 'jpg':
+        mimeType = 'image/jpeg';
+        break;
+      default:
+        mimeType = 'image/png';
+        break;
+      }
+    }
+    var downloadMime = 'image/octet-stream';
+    var imageData = cnv.toDataURL(mimeType);
+    imageData = imageData.replace(mimeType, downloadMime);
+
+    p5.prototype.downloadFile(imageData, filename, extension);
+  }
+};
+
+/**
+ *  Capture a sequence of frames that can be used to create a movie.
+ *  Accepts a callback. For example, you may wish to send the frames
+ *  to a server where they can be stored or converted into a movie.
+ *  If no callback is provided, the browser will attempt to download
+ *  all of the images that have just been created.
+ *
+ *  @method saveFrames
+ *  @param  {String}   filename
+ *  @param  {String}   extension 'jpg' or 'png'
+ *  @param  {Number}   duration  Duration in seconds to save the frames for.
+ *  @param  {Number}   framerate  Framerate to save the frames in.
+ *  @param  {Function} [callback] A callback function that will be executed
+                                  to handle the image data. This function
+                                  should accept an array as argument. The
+                                  array will contain the spcecified number of
+                                  frames of objects. Each object have three
+                                  properties: imageData - an
+                                  image/octet-stream, filename and extension.
+ */
+p5.prototype.saveFrames = function(fName, ext, _duration, _fps, callback) {
+  var duration = _duration || 3;
+  duration = p5.prototype.constrain(duration, 0, 15);
+  duration = duration * 1000;
+  var fps = _fps || 15;
+  fps = p5.prototype.constrain(fps, 0, 22);
+  var count = 0;
+
+  var makeFrame = p5.prototype._makeFrame;
+  var cnv = this._curElement.elt;
+  var frameFactory = setInterval(function(){
+    makeFrame(fName + count, ext, cnv);
+    count++;
+  },1000/fps);
+
+  setTimeout(function(){
+    clearInterval(frameFactory);
+    if (callback) {
+      callback(frames);
+    }
+    else {
+      for (var i = 0; i < frames.length; i++) {
+        var f = frames[i];
+        p5.prototype.downloadFile(f.imageData, f.filename, f.ext);
+      }
+    }
+    frames = []; // clear frames
+  }, duration + 0.01);
+};
+
+p5.prototype._makeFrame = function(filename, extension, _cnv) {
+  var cnv;
+  if (this) {
+    cnv = this._curElement.elt;
+  } else {
+    cnv = _cnv;
+  }
+  var mimeType;
+  if (!extension) {
+    extension = 'png';
+    mimeType = 'image/png';
+  }
+  else {
+    switch(extension.toLowerCase()){
+    case 'png':
+      mimeType = 'image/png';
+      break;
+    case 'jpeg':
+      mimeType = 'image/jpeg';
+      break;
+    case 'jpg':
+      mimeType = 'image/jpeg';
+      break;
+    default:
+      mimeType = 'image/png';
+      break;
+    }
+  }
+  var downloadMime = 'image/octet-stream';
+  var imageData = cnv.toDataURL(mimeType);
+  imageData = imageData.replace(mimeType, downloadMime);
+
+  var thisFrame = {};
+  thisFrame.imageData = imageData;
+  thisFrame.filename = filename;
+  thisFrame.ext = extension;
+  frames.push(thisFrame);
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],67:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Loading & Displaying
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+var canvas = _dereq_('../core/canvas');
+var constants = _dereq_('../core/constants');
+
+_dereq_('../core/error_helpers');
+
+/**
+ * Loads an image from a path and creates a p5.Image from it.
+ * <br><br>
+ * The image may not be immediately available for rendering
+ * If you want to ensure that the image is ready before doing
+ * anything with it, place the loadImage() call in preload().
+ * You may also supply a callback function to handle the image when it's ready.
+ * <br><br>
+ * The path to the image should be relative to the HTML file
+ * that links in your sketch. Loading an from a URL or other
+ * remote location may be blocked due to your browser's built-in
+ * security.
+ *
+ * @method loadImage
+ * @param  {String} path Path of the image to be loaded
+ * @param  {Function(p5.Image)} [successCallback] Function to be called once
+ *                                the image is loaded. Will be passed the
+ *                                p5.Image.
+ * @param  {Function(Event)}    [failureCallback] called with event error if
+ *                                the image fails to load.
+ * @return {p5.Image}             the p5.Image object
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ *   image(img, 0, 0);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * function setup() {
+ *   // here we use a callback to display the image after loading
+ *   loadImage("assets/laDefense.jpg", function(img) {
+ *     image(img, 0, 0);
+ *   });
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.loadImage = function(path, successCallback, failureCallback) {
+  var img = new Image();
+  var pImg = new p5.Image(1, 1, this);
+  var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+  img.onload = function() {
+    pImg.width = pImg.canvas.width = img.width;
+    pImg.height = pImg.canvas.height = img.height;
+
+    // Draw the image into the backing canvas of the p5.Image
+    pImg.drawingContext.drawImage(img, 0, 0);
+
+    if (typeof successCallback === 'function') {
+      successCallback(pImg);
+    }
+    if (decrementPreload && (successCallback !== decrementPreload)) {
+      decrementPreload();
+    }
+  };
+  img.onerror = function(e) {
+    p5._friendlyFileLoadError(0,img.src);
+    // don't get failure callback mixed up with decrementPreload
+    if ((typeof failureCallback === 'function') &&
+      (failureCallback !== decrementPreload)) {
+      failureCallback(e);
+    }
+  };
+
+  //set crossOrigin in case image is served which CORS headers
+  //this will let us draw to canvas without tainting it.
+  //see https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
+  // When using data-uris the file will be loaded locally
+  // so we don't need to worry about crossOrigin with base64 file types
+  if(path.indexOf('data:image/') !== 0) {
+    img.crossOrigin = 'Anonymous';
+  }
+
+  //start loading the image
+  img.src = path;
+
+  return pImg;
+};
+
+/**
+ * Validates clipping params. Per drawImage spec sWidth and sHight cannot be
+ * negative or greater than image intrinsic width and height
+ * @private
+ * @param {Number} sVal
+ * @param {Number} iVal
+ * @returns {Number}
+ * @private
+ */
+function _sAssign(sVal, iVal) {
+  if (sVal > 0 && sVal < iVal) {
+    return sVal;
+  }
+  else {
+    return iVal;
+  }
+}
+
+/**
+ * Draw an image to the main canvas of the p5js sketch
+ *
+ * @method image
+ * @param  {p5.Image} img    the image to display
+ * @param  {Number}   [sx=0]   The X coordinate of the top left corner of the
+ *                             sub-rectangle of the source image to draw into
+ *                             the destination canvas.
+ * @param  {Number}   [sy=0]   The Y coordinate of the top left corner of the
+ *                             sub-rectangle of the source image to draw into
+ *                             the destination canvas.
+ * @param {Number} [sWidth=img.width] The width of the sub-rectangle of the
+ *                                    source image to draw into the destination
+ *                                    canvas.
+ * @param {Number} [sHeight=img.height] The height of the sub-rectangle of the
+ *                                      source image to draw into the
+ *                                      destination context.
+ * @param  {Number}   [dx=0]    The X coordinate in the destination canvas at
+ *                              which to place the top-left corner of the
+ *                              source image.
+ * @param  {Number}   [dy=0]    The Y coordinate in the destination canvas at
+ *                              which to place the top-left corner of the
+ *                              source image.
+ * @param  {Number}   [dWidth]  The width to draw the image in the destination
+ *                              canvas. This allows scaling of the drawn image.
+ * @param  {Number}   [dHeight] The height to draw the image in the destination
+ *                              canvas. This allows scaling of the drawn image.
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ *   image(img, 0, 0);
+ *   image(img, 0, 0, 100, 100);
+ *   image(img, 0, 0, 100, 100, 0, 0, 100, 100);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * function setup() {
+ *   // here we use a callback to display the image after loading
+ *   loadImage("assets/laDefense.jpg", function(img) {
+ *     image(img, 0, 0);
+ *   });
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.image =
+  function(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) {
+  // Temporarily disabling until options for p5.Graphics are added.
+  // var args = new Array(arguments.length);
+  // for (var i = 0; i < args.length; ++i) {
+  //   args[i] = arguments[i];
+  // }
+  // this._validateParameters(
+  //   'image',
+  //   args,
+  //   [
+  //     ['p5.Image', 'Number', 'Number'],
+  //     ['p5.Image', 'Number', 'Number', 'Number', 'Number']
+  //   ]
+  // );
+
+  // set defaults per spec: https://goo.gl/3ykfOq
+  if (arguments.length <= 5) {
+    dx = sx || 0;
+    dy = sy || 0;
+    sx = 0;
+    sy = 0;
+    if (img.elt && img.elt.videoWidth && !img.canvas) { // video no canvas
+      var actualW = img.elt.videoWidth;
+      var actualH = img.elt.videoHeight;
+      dWidth = sWidth || img.elt.width;
+      dHeight = sHeight || img.elt.width*actualH/actualW;
+      sWidth = actualW;
+      sHeight = actualH;
+    } else {
+      dWidth = sWidth || img.width;
+      dHeight = sHeight || img.height;
+      sWidth = img.width;
+      sHeight = img.height;
+    }
+  } else if (arguments.length === 9) {
+    sx = sx || 0;
+    sy = sy || 0;
+    sWidth = _sAssign(sWidth, img.width);
+    sHeight = _sAssign(sHeight, img.height);
+
+    dx = dx || 0;
+    dy = dy || 0;
+    dWidth = dWidth || img.width;
+    dHeight = dHeight || img.height;
+  } else {
+    throw 'Wrong number of arguments to image()';
+  }
+
+  var vals = canvas.modeAdjust(dx, dy, dWidth, dHeight,
+    this._renderer._imageMode);
+
+  // tint the image if there is a tint
+  this._renderer.image(img, sx, sy, sWidth, sHeight, vals.x, vals.y, vals.w,
+    vals.h);
+};
+
+/**
+ * Sets the fill value for displaying images. Images can be tinted to
+ * specified colors or made transparent by including an alpha value.
+ * <br><br>
+ * To apply transparency to an image without affecting its color, use
+ * white as the tint color and specify an alpha value. For instance,
+ * tint(255, 128) will make an image 50% transparent (assuming the default
+ * alpha range of 0-255, which can be changed with colorMode()).
+ * <br><br>
+ * The value for the gray parameter must be less than or equal to the current
+ * maximum value as specified by colorMode(). The default maximum value is
+ * 255.
+ *
+ * @method tint
+ * @param {Number|Array} v1   gray value, red or hue value (depending on the
+ *                            current color mode), or color Array
+ * @param {Number|Array} [v2] green or saturation value (depending on the
+ *                            current color mode)
+ * @param {Number|Array} [v3] blue or brightness value (depending on the
+ *                            current color mode)
+ * @param {Number|Array} [a]  opacity of the background
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ *   image(img, 0, 0);
+ *   tint(0, 153, 204);  // Tint blue
+ *   image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ *   image(img, 0, 0);
+ *   tint(0, 153, 204, 126);  // Tint blue and set transparency
+ *   image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ *   image(img, 0, 0);
+ *   tint(255, 126);  // Apply transparency without changing color
+ *   image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.tint = function () {
+  var c = this.color.apply(this, arguments);
+  this._renderer._tint = c.levels;
+};
+
+/**
+ * Removes the current fill value for displaying images and reverts to
+ * displaying images with their original hues.
+ *
+ * @method noTint
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *   tint(0, 153, 204);  // Tint blue
+ *   image(img, 0, 0);
+ *   noTint();  // Disable tint
+ *   image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noTint = function() {
+  this._renderer._tint = null;
+};
+
+/**
+ * Apply the current tint color to the input image, return the resulting
+ * canvas.
+ *
+ * @param {p5.Image} The image to be tinted
+ * @return {canvas} The resulting tinted canvas
+ *
+ */
+p5.prototype._getTintedImageCanvas = function(img) {
+  if (!img.canvas) {
+    return img;
+  }
+  var pixels = Filters._toPixels(img.canvas);
+  var tmpCanvas = document.createElement('canvas');
+  tmpCanvas.width = img.canvas.width;
+  tmpCanvas.height = img.canvas.height;
+  var tmpCtx = tmpCanvas.getContext('2d');
+  var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height);
+  var newPixels = id.data;
+
+  for(var i = 0; i < pixels.length; i += 4) {
+    var r = pixels[i];
+    var g = pixels[i+1];
+    var b = pixels[i+2];
+    var a = pixels[i+3];
+
+    newPixels[i] = r*this._renderer._tint[0]/255;
+    newPixels[i+1] = g*this._renderer._tint[1]/255;
+    newPixels[i+2] = b*this._renderer._tint[2]/255;
+    newPixels[i+3] = a*this._renderer._tint[3]/255;
+  }
+
+  tmpCtx.putImageData(id, 0, 0);
+  return tmpCanvas;
+};
+
+/**
+ * Set image mode. Modifies the location from which images are drawn by
+ * changing the way in which parameters given to image() are interpreted.
+ * The default mode is imageMode(CORNER), which interprets the second and
+ * third parameters of image() as the upper-left corner of the image. If
+ * two additional parameters are specified, they are used to set the image's
+ * width and height.
+ * <br><br>
+ * imageMode(CORNERS) interprets the second and third parameters of image()
+ * as the location of one corner, and the fourth and fifth parameters as the
+ * opposite corner.
+ * <br><br>
+ * imageMode(CENTER) interprets the second and third parameters of image()
+ * as the image's center point. If two additional parameters are specified,
+ * they are used to set the image's width and height.
+ *
+ * @method imageMode
+ * @param {String} m The mode: either CORNER, CORNERS, or CENTER.
+ * @example
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *   imageMode(CORNER);
+ *   image(img, 10, 10, 50, 50);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *   imageMode(CORNERS);
+ *   image(img, 10, 10, 90, 40);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *   imageMode(CENTER);
+ *   image(img, 50, 50, 80, 80);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.imageMode = function(m) {
+  if (m === constants.CORNER ||
+    m === constants.CORNERS ||
+    m === constants.CENTER) {
+    this._renderer._imageMode = m;
+  }
+};
+
+
+module.exports = p5;
+
+},{"../core/canvas":46,"../core/constants":47,"../core/core":48,"../core/error_helpers":51,"./filters":65}],68:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Image
+ * @requires core
+ * @requires constants
+ * @requires filters
+ */
+
+/**
+ * This module defines the p5.Image class and P5 methods for
+ * drawing images to the main display canvas.
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+
+/*
+ * Class methods
+ */
+
+/**
+ * Creates a new p5.Image. A p5.Image is a canvas backed representation of an
+ * image.
+ * <br><br>
+ * p5 can display .gif, .jpg and .png images. Images may be displayed
+ * in 2D and 3D space. Before an image is used, it must be loaded with the
+ * loadImage() function. The p5.Image class contains fields for the width and
+ * height of the image, as well as an array called pixels[] that contains the
+ * values for every pixel in the image.
+ * <br><br>
+ * The methods described below allow easy access to the image's pixels and
+ * alpha channel and simplify the process of compositing.
+ * <br><br>
+ * Before using the pixels[] array, be sure to use the loadPixels() method on
+ * the image to make sure that the pixel data is properly loaded.
+ *
+ * @class p5.Image
+ * @constructor
+ * @param {Number} width
+ * @param {Number} height
+ * @param {Object} pInst An instance of a p5 sketch.
+ */
+p5.Image = function(width, height){
+  /**
+   * Image width.
+   * @property width
+   * @example
+   * <div><code>
+   * var img;
+   * function preload() {
+   *   img = loadImage("assets/rockies.jpg");
+   * }
+   *
+   * function setup() {
+   *   createCanvas(100, 100);
+   *   image(img, 0, 0);
+   *   for (var i=0; i < img.width; i++) {
+   *     var c = img.get(i, img.height/2);
+   *     stroke(c);
+   *     line(i, height/2, i, height);
+   *   }
+   * }
+   * </code></div>
+   */
+  this.width = width;
+  /**
+   * Image height.
+   * @property height
+   * @example
+   * <div><code>
+   * var img;
+   * function preload() {
+   *   img = loadImage("assets/rockies.jpg");
+   * }
+   *
+   * function setup() {
+   *   createCanvas(100, 100);
+   *   image(img, 0, 0);
+   *   for (var i=0; i < img.height; i++) {
+   *     var c = img.get(img.width/2, i);
+   *     stroke(c);
+   *     line(0, i, width/2, i);
+   *   }
+   * }
+   * </code></div>
+   */
+  this.height = height;
+  this.canvas = document.createElement('canvas');
+  this.canvas.width = this.width;
+  this.canvas.height = this.height;
+  this.drawingContext = this.canvas.getContext('2d');
+  this._pixelDensity = 1;
+  //used for webgl texturing only
+  this.isTexture = false;
+  /**
+   * Array containing the values for all the pixels in the display window.
+   * These values are numbers. This array is the size (include an appropriate
+   * factor for pixelDensity) of the display window x4,
+   * representing the R, G, B, A values in order for each pixel, moving from
+   * left to right across each row, then down each column. Retina and other
+   * high denisty displays may have more pixels[] (by a factor of
+   * pixelDensity^2).
+   * For example, if the image is 100x100 pixels, there will be 40,000. With
+   * pixelDensity = 2, there will be 160,000. The first four values
+   * (indices 0-3) in the array will be the R, G, B, A values of the pixel at
+   * (0, 0). The second four values (indices 4-7) will contain the R, G, B, A
+   * values of the pixel at (1, 0). More generally, to set values for a pixel
+   * at (x, y):
+   * <code><pre>var d = pixelDensity;
+   * for (var i = 0; i < d; i++) {
+   *   for (var j = 0; j < d; j++) {
+   *     // loop over
+   *     idx = 4*((y * d + j) * width * d + (x * d + i));
+   *     pixels[idx] = r;
+   *     pixels[idx+1] = g;
+   *     pixels[idx+2] = b;
+   *     pixels[idx+3] = a;
+   *   }
+   * }
+   * </pre></code>
+   * <br><br>
+   * Before accessing this array, the data must loaded with the loadPixels()
+   * function. After the array data has been modified, the updatePixels()
+   * function must be run to update the changes.
+   * @property pixels[]
+   * @example
+   * <div>
+   * <code>
+   * img = createImage(66, 66);
+   * img.loadPixels();
+   * for (i = 0; i < img.width; i++) {
+   *   for (j = 0; j < img.height; j++) {
+   *     img.set(i, j, color(0, 90, 102));
+   *   }
+   * }
+   * img.updatePixels();
+   * image(img, 17, 17);
+   * </code>
+   * </div>
+   * <div>
+   * <code>
+   * var pink = color(255, 102, 204);
+   * img = createImage(66, 66);
+   * img.loadPixels();
+   * for (var i = 0; i < 4*(width*height/2); i+=4) {
+   *   img.pixels[i] = red(pink);
+   *   img.pixels[i+1] = green(pink);
+   *   img.pixels[i+2] = blue(pink);
+   *   img.pixels[i+3] = alpha(pink);
+   * }
+   * img.updatePixels();
+   * image(img, 17, 17);
+   * </code>
+   * </div>
+   */
+  this.pixels = [];
+};
+
+/**
+ * Helper fxn for sharing pixel methods
+ *
+ */
+p5.Image.prototype._setProperty = function (prop, value) {
+  this[prop] = value;
+};
+
+/**
+ * Loads the pixels data for this image into the [pixels] attribute.
+ *
+ * @method loadPixels
+ * @example
+ * <div><code>
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ *   myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   myImage.loadPixels();
+ *   halfImage = 4 * width * height/2;
+ *   for(var i = 0; i < halfImage; i++){
+ *     myImage.pixels[i+halfImage] = myImage.pixels[i];
+ *   }
+ *   myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ *   image(myImage, 0, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.loadPixels = function(){
+  p5.Renderer2D.prototype.loadPixels.call(this);
+};
+
+/**
+ * Updates the backing canvas for this image with the contents of
+ * the [pixels] array.
+ *
+ * @method updatePixels
+ * @param {Integer|undefined} x x-offset of the target update area for the
+ *                              underlying canvas
+ * @param {Integer|undefined} y y-offset of the target update area for the
+ *                              underlying canvas
+ * @param {Integer|undefined} w height of the target update area for the
+ *                              underlying canvas
+ * @param {Integer|undefined} h height of the target update area for the
+ *                              underlying canvas
+ * @example
+ * <div><code>
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ *   myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   myImage.loadPixels();
+ *   halfImage = 4 * width * height/2;
+ *   for(var i = 0; i < halfImage; i++){
+ *     myImage.pixels[i+halfImage] = myImage.pixels[i];
+ *   }
+ *   myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ *   image(myImage, 0, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.updatePixels = function(x, y, w, h){
+  p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
+};
+
+/**
+ * Get a region of pixels from an image.
+ *
+ * If no params are passed, those whole image is returned,
+ * if x and y are the only params passed a single pixel is extracted
+ * if all params are passed a rectangle region is extracted and a p5.Image
+ * is returned.
+ *
+ * Returns undefined if the region is outside the bounds of the image
+ *
+ * @method get
+ * @param  {Number}               [x] x-coordinate of the pixel
+ * @param  {Number}               [y] y-coordinate of the pixel
+ * @param  {Number}               [w] width
+ * @param  {Number}               [h] height
+ * @return {Array/Color | p5.Image}     color of pixel at x,y in array format
+ *                                    [R, G, B, A] or p5.Image
+ * @example
+ * <div><code>
+ * var myImage;
+ * var c;
+ *
+ * function preload() {
+ *   myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   background(myImage);
+ *   noStroke();
+ *   c = myImage.get(60, 90);
+ *   fill(c);
+ *   rect(25, 25, 50, 50);
+ * }
+ *
+ * //get() returns color here
+ * </code></div>
+ */
+p5.Image.prototype.get = function(x, y, w, h){
+  return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
+};
+
+/**
+ * Set the color of a single pixel or write an image into
+ * this p5.Image.
+ *
+ * Note that for a large number of pixels this will
+ * be slower than directly manipulating the pixels array
+ * and then calling updatePixels().
+ *
+ * @method set
+ * @param {Number}              x x-coordinate of the pixel
+ * @param {Number}              y y-coordinate of the pixel
+ * @param {Number|Array|Object}   a grayscale value | pixel array |
+ *                                a p5.Color | image to copy
+ * @example
+ * <div>
+ * <code>
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ *   for (j = 0; j < img.height; j++) {
+ *     img.set(i, j, color(0, 90, 102, i % img.width * 2));
+ *   }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * image(img, 34, 34);
+ * </code>
+ * </div>
+ */
+p5.Image.prototype.set = function(x, y, imgOrCol){
+  p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
+};
+
+/**
+ * Resize the image to a new width and height. To make the image scale
+ * proportionally, use 0 as the value for the wide or high parameter.
+ * For instance, to make the width of an image 150 pixels, and change
+ * the height using the same proportion, use resize(150, 0).
+ *
+ * @method resize
+ * @param {Number} width the resized image width
+ * @param {Number} height the resized image height
+ * @example
+ * <div><code>
+ * var img;
+ *
+ * function setup() {
+ *   img = loadImage("assets/rockies.jpg");
+ * }
+
+ * function draw() {
+ *   image(img, 0, 0);
+ * }
+ *
+ * function mousePressed() {
+ *   img.resize(50, 100);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.resize = function(width, height){
+
+  // Copy contents to a temporary canvas, resize the original
+  // and then copy back.
+  //
+  // There is a faster approach that involves just one copy and swapping the
+  // this.canvas reference. We could switch to that approach if (as i think
+  // is the case) there an expectation that the user would not hold a
+  // reference to the backing canvas of a p5.Image. But since we do not
+  // enforce that at the moment, I am leaving in the slower, but safer
+  // implementation.
+
+  // auto-resize
+  if (width === 0 && height === 0) {
+    width = this.canvas.width;
+    height = this.canvas.height;
+  } else if (width === 0) {
+    width = this.canvas.width * height / this.canvas.height;
+  } else if (height === 0) {
+    height = this.canvas.height * width / this.canvas.width;
+  }
+
+  var tempCanvas = document.createElement('canvas');
+  tempCanvas.width = width;
+  tempCanvas.height = height;
+  tempCanvas.getContext('2d').drawImage(this.canvas,
+    0, 0, this.canvas.width, this.canvas.height,
+    0, 0, tempCanvas.width, tempCanvas.height
+  );
+
+
+  // Resize the original canvas, which will clear its contents
+  this.canvas.width = this.width = width;
+  this.canvas.height = this.height = height;
+
+  //Copy the image back
+
+  this.drawingContext.drawImage(tempCanvas,
+    0, 0, width, height,
+    0, 0, width, height
+  );
+
+  if(this.pixels.length > 0){
+    this.loadPixels();
+  }
+};
+
+/**
+ * Copies a region of pixels from one image to another. If no
+ * srcImage is specified this is used as the source. If the source
+ * and destination regions aren't the same size, it will
+ * automatically resize source pixels to fit the specified
+ * target region.
+ *
+ * @method copy
+ * @param  {p5.Image|undefined} srcImage source image
+ * @param  {Integer} sx X coordinate of the source's upper left corner
+ * @param  {Integer} sy Y coordinate of the source's upper left corner
+ * @param  {Integer} sw source image width
+ * @param  {Integer} sh source image height
+ * @param  {Integer} dx X coordinate of the destination's upper left corner
+ * @param  {Integer} dy Y coordinate of the destination's upper left corner
+ * @param  {Integer} dw destination image width
+ * @param  {Integer} dh destination image height
+ * @example
+ * <div><code>
+ * var photo;
+ * var bricks;
+ * var x;
+ * var y;
+ *
+ * function preload() {
+ *   photo = loadImage("assets/rockies.jpg");
+ *   bricks = loadImage("assets/bricks.jpg");
+ * }
+ *
+ * function setup() {
+ *   x = bricks.width/2;
+ *   y = bricks.height/2;
+ *   photo.copy(bricks, 0, 0, x, y, 0, 0, x, y);
+ *   image(photo, 0, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.copy = function () {
+  p5.prototype.copy.apply(this, arguments);
+};
+
+/**
+ * Masks part of an image from displaying by loading another
+ * image and using it's blue channel as an alpha channel for
+ * this image.
+ *
+ * @method mask
+ * @param {p5.Image} srcImage source image
+ * @example
+ * <div><code>
+ * var photo, maskImage;
+ * function preload() {
+ *   photo = loadImage("assets/rockies.jpg");
+ *   maskImage = loadImage("assets/mask2.png");
+ * }
+ *
+ * function setup() {
+ *   createCanvas(100, 100);
+ *   photo.mask(maskImage);
+ *   image(photo, 0, 0);
+ * }
+ * </code></div>
+ *
+ * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
+ *
+ */
+// TODO: - Accept an array of alpha values.
+//       - Use other channels of an image. p5 uses the
+//       blue channel (which feels kind of arbitrary). Note: at the
+//       moment this method does not match native processings original
+//       functionality exactly.
+p5.Image.prototype.mask = function(p5Image) {
+  if(p5Image === undefined){
+    p5Image = this;
+  }
+  var currBlend = this.drawingContext.globalCompositeOperation;
+
+  var scaleFactor = 1;
+  if (p5Image instanceof p5.Renderer) {
+    scaleFactor = p5Image._pInst._pixelDensity;
+  }
+
+  var copyArgs = [
+    p5Image,
+    0,
+    0,
+    scaleFactor*p5Image.width,
+    scaleFactor*p5Image.height,
+    0,
+    0,
+    this.width,
+    this.height
+  ];
+
+  this.drawingContext.globalCompositeOperation = 'destination-in';
+  this.copy.apply(this, copyArgs);
+  this.drawingContext.globalCompositeOperation = currBlend;
+};
+
+/**
+ * Applies an image filter to a p5.Image
+ *
+ * @method filter
+ * @param {String} operation one of threshold, gray, invert, posterize and
+ *                           opaque see Filters.js for docs on each available
+ *                           filter
+ * @param {Number|undefined} value
+ * @example
+ * <div><code>
+ * var photo1;
+ * var photo2;
+ *
+ * function preload() {
+ *   photo1 = loadImage("assets/rockies.jpg");
+ *   photo2 = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   photo2.filter("gray");
+ *   image(photo1, 0, 0);
+ *   image(photo2, width/2, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.filter = function(operation, value) {
+  Filters.apply(this.canvas, Filters[operation.toLowerCase()], value);
+};
+
+/**
+ * Copies a region of pixels from one image to another, using a specified
+ * blend mode to do the operation.
+ *
+ * @method blend
+ * @param  {p5.Image|undefined} srcImage source image
+ * @param  {Integer} sx X coordinate of the source's upper left corner
+ * @param  {Integer} sy Y coordinate of the source's upper left corner
+ * @param  {Integer} sw source image width
+ * @param  {Integer} sh source image height
+ * @param  {Integer} dx X coordinate of the destination's upper left corner
+ * @param  {Integer} dy Y coordinate of the destination's upper left corner
+ * @param  {Integer} dw destination image width
+ * @param  {Integer} dh destination image height
+ * @param  {Integer} blendMode the blend mode
+ *
+ * Available blend modes are: normal | multiply | screen | overlay |
+ *            darken | lighten | color-dodge | color-burn | hard-light |
+ *            soft-light | difference | exclusion | hue | saturation |
+ *            color | luminosity
+ *
+ *
+ * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
+ *
+ */
+p5.Image.prototype.blend = function() {
+  p5.prototype.blend.apply(this, arguments);
+};
+
+/**
+ * Saves the image to a file and force the browser to download it.
+ * Accepts two strings for filename and file extension
+ * Supports png (default) and jpg.
+ *
+ * @method save
+ * @param {String} filename give your file a name
+ * @param  {String} extension 'png' or 'jpg'
+ * @example
+ * <div><code>
+ * var photo;
+ *
+ * function preload() {
+ *   photo = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function draw() {
+ *   image(photo, 0, 0);
+ * }
+ *
+ * function keyTyped() {
+ *   if (key == 's') {
+ *     photo.save("photo", "png");
+ *   }
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.save = function(filename, extension) {
+  var mimeType;
+  if (!extension) {
+    extension = 'png';
+    mimeType = 'image/png';
+  }
+  else {
+    // en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support
+    switch(extension.toLowerCase()){
+    case 'png':
+      mimeType = 'image/png';
+      break;
+    case 'jpeg':
+      mimeType = 'image/jpeg';
+      break;
+    case 'jpg':
+      mimeType = 'image/jpeg';
+      break;
+    default:
+      mimeType = 'image/png';
+      break;
+    }
+  }
+  var downloadMime = 'image/octet-stream';
+  var imageData = this.canvas.toDataURL(mimeType);
+  imageData = imageData.replace(mimeType, downloadMime);
+
+  //Make the browser download the file
+  p5.prototype.downloadFile(imageData, filename, extension);
+};
+
+/**
+ * creates a gl texture
+ * used in WEBGL mode only
+ * @param  {[type]} tex [description]
+ * @return {[type]}     [description]
+ */
+p5.Image.prototype.createTexture = function(tex){
+  //this.texture = tex;
+  return this;
+};
+
+module.exports = p5.Image;
+
+},{"../core/core":48,"./filters":65}],69:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Pixels
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+_dereq_('../color/p5.Color');
+
+/**
+ * <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference
+ * /Global_Objects/Uint8ClampedArray' target='_blank'>Uint8ClampedArray</a>
+ * containing the values for all the pixels in the display window.
+ * These values are numbers. This array is the size (include an appropriate
+ * factor for pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. Retina and other
+ * high denisty displays will have more pixels[] (by a factor of
+ * pixelDensity^2).
+ * For example, if the image is 100x100 pixels, there will be 40,000. On a
+ * retina display, there will be 160,000.
+ * <br><br>
+ * The first four values (indices 0-3) in the array will be the R, G, B, A
+ * values of the pixel at (0, 0). The second four values (indices 4-7) will
+ * contain the R, G, B, A values of the pixel at (1, 0). More generally, to
+ * set values for a pixel at (x, y):
+ * <code><pre>
+ * var d = pixelDensity;
+ * for (var i = 0; i < d; i++) {
+ *   for (var j = 0; j < d; j++) {
+ *     // loop over
+ *     idx = 4 * ((y * d + j) * width * d + (x * d + i));
+ *     pixels[idx] = r;
+ *     pixels[idx+1] = g;
+ *     pixels[idx+2] = b;
+ *     pixels[idx+3] = a;
+ *   }
+ * }
+ * </pre></code>
+ *
+ * <p>While the above method is complex, it is flexible enough to work with
+ * any pixelDensity. Note that set() will automatically take care of
+ * setting all the appropriate values in pixels[] for a given (x, y) at
+ * any pixelDensity, but the performance may not be as fast when lots of
+ * modifications are made to the pixel array.
+ * <br><br>
+ * Before accessing this array, the data must loaded with the loadPixels()
+ * function. After the array data has been modified, the updatePixels()
+ * function must be run to update the changes.
+ * <br><br>
+ * Note that this is not a standard javascript array.  This means that
+ * standard javascript functions such as <code>slice()</code> or
+ * <code>arrayCopy()</code> do not
+ * work.</p>
+ *
+ * @property pixels[]
+ * @example
+ * <div>
+ * <code>
+ * var pink = color(255, 102, 204);
+ * loadPixels();
+ * var d = pixelDensity();
+ * var halfImage = 4 * (width * d) * (height/2 * d);
+ * for (var i = 0; i < halfImage; i+=4) {
+ *   pixels[i] = red(pink);
+ *   pixels[i+1] = green(pink);
+ *   pixels[i+2] = blue(pink);
+ *   pixels[i+3] = alpha(pink);
+ * }
+ * updatePixels();
+ * </code>
+ * </div>
+ */
+p5.prototype.pixels = [];
+
+/**
+ * Copies a region of pixels from one image to another, using a specified
+ * blend mode to do the operation.<br><br>
+ * Available blend modes are: BLEND | DARKEST | LIGHTEST | DIFFERENCE |
+ * MULTIPLY| EXCLUSION | SCREEN | REPLACE | OVERLAY | HARD_LIGHT |
+ * SOFT_LIGHT | DODGE | BURN | ADD | NORMAL
+ *
+ *
+ * @method blend
+ * @param  {p5.Image|undefined} srcImage source image
+ * @param  {Integer} sx X coordinate of the source's upper left corner
+ * @param  {Integer} sy Y coordinate of the source's upper left corner
+ * @param  {Integer} sw source image width
+ * @param  {Integer} sh source image height
+ * @param  {Integer} dx X coordinate of the destination's upper left corner
+ * @param  {Integer} dy Y coordinate of the destination's upper left corner
+ * @param  {Integer} dw destination image width
+ * @param  {Integer} dh destination image height
+ * @param  {Integer} blendMode the blend mode
+ *
+ * @example
+ * <div><code>
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ *   img0 = loadImage("assets/rockies.jpg");
+ *   img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ *   background(img0);
+ *   image(img1, 0, 0);
+ *   blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST);
+ * }
+ * </code></div>
+ * <div><code>
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ *   img0 = loadImage("assets/rockies.jpg");
+ *   img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ *   background(img0);
+ *   image(img1, 0, 0);
+ *   blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST);
+ * }
+ * </code></div>
+ * <div><code>
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ *   img0 = loadImage("assets/rockies.jpg");
+ *   img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ *   background(img0);
+ *   image(img1, 0, 0);
+ *   blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, ADD);
+ * }
+ * </code></div>
+ */
+p5.prototype.blend = function() {
+  this._renderer.blend.apply(this._renderer, arguments);
+};
+
+/**
+ * Copies a region of the canvas to another region of the canvas
+ * and copies a region of pixels from an image used as the srcImg parameter
+ * into the canvas srcImage is specified this is used as the source. If
+ * the source and destination regions aren't the same size, it will
+ * automatically resize source pixels to fit the specified
+ * target region.
+ *
+ * @method copy
+ * @param  {p5.Image|undefined} srcImage source image
+ * @param  {Integer} sx X coordinate of the source's upper left corner
+ * @param  {Integer} sy Y coordinate of the source's upper left corner
+ * @param  {Integer} sw source image width
+ * @param  {Integer} sh source image height
+ * @param  {Integer} dx X coordinate of the destination's upper left corner
+ * @param  {Integer} dy Y coordinate of the destination's upper left corner
+ * @param  {Integer} dw destination image width
+ * @param  {Integer} dh destination image height
+ *
+ * @example
+ * <div><code>
+ * var img;
+ *
+ * function preload() {
+ *   img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   background(img);
+ *   copy(img, 7, 22, 10, 10, 35, 25, 50, 50);
+ *   stroke(255);
+ *   noFill();
+ *   // Rectangle shows area being copied
+ *   rect(7, 22, 10, 10);
+ * }
+ * </code></div>
+ */
+p5.prototype.copy = function () {
+  p5.Renderer2D._copyHelper.apply(this, arguments);
+};
+
+/**
+ * Applies a filter to the canvas.
+ * <br><br>
+ *
+ * The presets options are:
+ * <br><br>
+ *
+ * THRESHOLD
+ * Converts the image to black and white pixels depending if they are above or
+ * below the threshold defined by the level parameter. The parameter must be
+ * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used.
+ * <br><br>
+ *
+ * GRAY
+ * Converts any colors in the image to grayscale equivalents. No parameter
+ * is used.
+ * <br><br>
+ *
+ * OPAQUE
+ * Sets the alpha channel to entirely opaque. No parameter is used.
+ * <br><br>
+ *
+ * INVERT
+ * Sets each pixel to its inverse value. No parameter is used.
+ * <br><br>
+ *
+ * POSTERIZE
+ * Limits each channel of the image to the number of colors specified as the
+ * parameter. The parameter can be set to values between 2 and 255, but
+ * results are most noticeable in the lower ranges.
+ * <br><br>
+ *
+ * BLUR
+ * Executes a Guassian blur with the level parameter specifying the extent
+ * of the blurring. If no parameter is used, the blur is equivalent to
+ * Guassian blur of radius 1. Larger values increase the blur.
+ * <br><br>
+ *
+ * ERODE
+ * Reduces the light areas. No parameter is used.
+ * <br><br>
+ *
+ * DILATE
+ * Increases the light areas. No parameter is used.
+ *
+ * @method filter
+ * @param  {String} filterType
+ * @param  {Number} filterParam an optional parameter unique
+ *  to each filter, see above
+ *
+ *
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(THRESHOLD);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(GRAY);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(OPAQUE);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(INVERT);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(POSTERIZE,3);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(DILATE);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(BLUR,3);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ *  image(img, 0, 0);
+ *  filter(ERODE);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.filter = function(operation, value) {
+  Filters.apply(this.canvas, Filters[operation.toLowerCase()], value);
+};
+
+/**
+ * Returns an array of [R,G,B,A] values for any pixel or grabs a section of
+ * an image. If no parameters are specified, the entire image is returned.
+ * Use the x and y parameters to get the value of one pixel. Get a section of
+ * the display window by specifying additional w and h parameters. When
+ * getting an image, the x and y parameters define the coordinates for the
+ * upper-left corner of the image, regardless of the current imageMode().
+ * <br><br>
+ * If the pixel requested is outside of the image window, [0,0,0,255] is
+ * returned. To get the numbers scaled according to the current color ranges
+ * and taking into account colorMode, use getColor instead of get.
+ * <br><br>
+ * Getting the color of a single pixel with get(x, y) is easy, but not as fast
+ * as grabbing the data directly from pixels[]. The equivalent statement to
+ * get(x, y) using pixels[] with pixel density d is
+ * <code>[pixels[(y*width*d+x)*d],
+ * pixels[(y*width*d+x)*d+1],
+ * pixels[(y*width*d+x)*d+2],
+ * pixels[(y*width*d+x)*d+3]]</code>.
+ * <br><br>
+ * See the reference for pixels[] for more information.
+ *
+ * @method get
+ * @param  {Number}         [x] x-coordinate of the pixel
+ * @param  {Number}         [y] y-coordinate of the pixel
+ * @param  {Number}         [w] width
+ * @param  {Number}         [h] height
+ * @return {Array|p5.Image}     values of pixel at x,y in array format
+ *                              [R, G, B, A] or p5.Image
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/rockies.jpg");
+ * }
+ * function setup() {
+ *   image(img, 0, 0);
+ *   var c = get();
+ *   image(c, width/2, 0);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/rockies.jpg");
+ * }
+ * function setup() {
+ *   image(img, 0, 0);
+ *   var c = get(50, 90);
+ *   fill(c);
+ *   noStroke();
+ *   rect(25, 25, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.get = function(x, y, w, h){
+  return this._renderer.get(x, y, w, h);
+};
+
+/**
+ * Loads the pixel data for the display window into the pixels[] array. This
+ * function must always be called before reading from or writing to pixels[].
+ *
+ * @method loadPixels
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   image(img, 0, 0);
+ *   var d = pixelDensity();
+ *   var halfImage = 4 * (img.width * d) *
+       (img.height/2 * d);
+ *   loadPixels();
+ *   for (var i = 0; i < halfImage; i++) {
+ *     pixels[i+halfImage] = pixels[i];
+ *   }
+ *   updatePixels();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.loadPixels = function() {
+  this._renderer.loadPixels();
+};
+
+/**
+ * <p>Changes the color of any pixel, or writes an image directly to the
+ * display window.</p>
+ * <p>The x and y parameters specify the pixel to change and the c parameter
+ * specifies the color value. This can be a p5.Color object, or [R, G, B, A]
+ * pixel array. It can also be a single grayscale value.
+ * When setting an image, the x and y parameters define the coordinates for
+ * the upper-left corner of the image, regardless of the current imageMode().
+ * </p>
+ * <p>
+ * After using set(), you must call updatePixels() for your changes to
+ * appear.  This should be called once all pixels have been set.
+ * </p>
+ * <p>Setting the color of a single pixel with set(x, y) is easy, but not as
+ * fast as putting the data directly into pixels[]. Setting the pixels[]
+ * values directly may be complicated when working with a retina display,
+ * but will perform better when lots of pixels need to be set directly on
+ * every loop.</p>
+ * <p>See the reference for pixels[] for more information.</p>
+ *
+ * @method set
+ * @param {Number}              x x-coordinate of the pixel
+ * @param {Number}              y y-coordinate of the pixel
+ * @param {Number|Array|Object} c insert a grayscale value | a pixel array |
+ *                                a p5.Color object | a p5.Image to copy
+ * @example
+ * <div>
+ * <code>
+ * var black = color(0);
+ * set(30, 20, black);
+ * set(85, 20, black);
+ * set(85, 75, black);
+ * set(30, 75, black);
+ * updatePixels();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * for (var i = 30; i < width-15; i++) {
+ *   for (var j = 20; j < height-25; j++) {
+ *     var c = color(204-j, 153-i, 0);
+ *     set(i, j, c);
+ *   }
+ * }
+ * updatePixels();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   set(0, 0, img);
+ *   updatePixels();
+ *   line(0, 0, width, height);
+ *   line(0, height, width, 0);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.set = function (x, y, imgOrCol) {
+  this._renderer.set(x, y, imgOrCol);
+};
+/**
+ * Updates the display window with the data in the pixels[] array.
+ * Use in conjunction with loadPixels(). If you're only reading pixels from
+ * the array, there's no need to call updatePixels() — updating is only
+ * necessary to apply changes. updatePixels() should be called anytime the
+ * pixels array is manipulated or set() is called.
+ *
+ * @method updatePixels
+ * @param  {Number} [x]    x-coordinate of the upper-left corner of region
+ *                         to update
+ * @param  {Number} [y]    y-coordinate of the upper-left corner of region
+ *                         to update
+ * @param  {Number} [w]    width of region to update
+ * @param  {Number} [w]    height of region to update
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ *   img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ *   image(img, 0, 0);
+ *   var halfImage = 4 * (img.width * pixelDensity()) *
+ *     (img.height * pixelDensity()/2);
+ *   loadPixels();
+ *   for (var i = 0; i < halfImage; i++) {
+ *     pixels[i+halfImage] = pixels[i];
+ *   }
+ *   updatePixels();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.updatePixels = function (x, y, w, h) {
+  // graceful fail - if loadPixels() or set() has not been called, pixel
+  // array will be empty, ignore call to updatePixels()
+  if (this.pixels.length === 0) {
+    return;
+  }
+  this._renderer.updatePixels(x, y, w, h);
+};
+
+module.exports = p5;
+
+},{"../color/p5.Color":42,"../core/core":48,"./filters":65}],70:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Input
+ * @for p5
+ * @requires core
+ * @requires reqwest
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var reqwest = _dereq_('reqwest');
+var opentype = _dereq_('opentype.js');
+_dereq_('../core/error_helpers');
+
+/**
+ * Checks if we are in preload and returns the last arg which will be the
+ * _decrementPreload function if called from a loadX() function.  Should
+ * only be used in loadX() functions.
+ * @private
+ */
+p5._getDecrementPreload = function () {
+  var decrementPreload = arguments[arguments.length - 1];
+
+  // when in preload decrementPreload will always be the last arg as it is set
+  // with args.push() before invocation in _wrapPreload
+  if ((window.preload || (this && this.preload)) &&
+    typeof decrementPreload === 'function') {
+    return decrementPreload;
+  } else {
+    return null;
+  }
+};
+
+/**
+ * Loads an opentype font file (.otf, .ttf) from a file or a URL,
+ * and returns a PFont Object. This method is asynchronous,
+ * meaning it may not finish before the next line in your sketch
+ * is executed.
+ * <br><br>
+ * The path to the font should be relative to the HTML file
+ * that links in your sketch. Loading an from a URL or other
+ * remote location may be blocked due to your browser's built-in
+ * security.
+ *
+ * @method loadFont
+ * @param  {String}        path       name of the file or url to load
+ * @param  {Function}      [callback] function to be executed after
+ *                                    loadFont()
+ *                                    completes
+ * @return {Object}                   p5.Font object
+ * @example
+ *
+ * <p>Calling loadFont() inside preload() guarantees that the load
+ * operation will have completed before setup() and draw() are called.</p>
+ *
+ * <div><code>
+ * var myFont;
+ * function preload() {
+ *   myFont = loadFont('assets/AvenirNextLTPro-Demi.otf');
+ * }
+ *
+ * function setup() {
+ *   fill('#ED225D');
+ *   textFont(myFont);
+ *   textSize(36);
+ *   text('p5*js', 10, 50);
+ * }
+ * </code></div>
+ *
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ *
+ * <div><code>
+ * function setup() {
+ *   loadFont('assets/AvenirNextLTPro-Demi.otf', drawText);
+ * }
+ *
+ * function drawText(font) {
+ *   fill('#ED225D');
+ *   textFont(font, 36);
+ *   text('p5*js', 10, 50);
+ * }
+ *
+ * </code></div>
+ *
+ * <p>You can also use the string name of the font to style other HTML
+ * elements.</p>
+ *
+ * <div><code>
+ * var myFont;
+ *
+ * function preload() {
+ *   myFont = loadFont('assets/Avenir.otf');
+ * }
+ *
+ * function setup() {
+ *   var myDiv = createDiv('hello there');
+ *   myDiv.style('font-family', 'Avenir');
+ * }
+ * </code></div>
+ */
+p5.prototype.loadFont = function (path, onSuccess, onError) {
+
+  var p5Font = new p5.Font(this);
+  var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+  opentype.load(path, function (err, font) {
+
+    if (err) {
+
+      if ((typeof onError !== 'undefined') && (onError !== decrementPreload)) {
+        return onError(err);
+      }
+      throw err;
+    }
+
+    p5Font.font = font;
+
+    if (typeof onSuccess !== 'undefined') {
+      onSuccess(p5Font);
+    }
+
+    if (decrementPreload && (onSuccess !== decrementPreload)) {
+      decrementPreload();
+    }
+
+    // check that we have an acceptable font type
+    var validFontTypes = [ 'ttf', 'otf', 'woff', 'woff2' ],
+      fileNoPath = path.split('\\').pop().split('/').pop(),
+      lastDotIdx = fileNoPath.lastIndexOf('.'), fontFamily, newStyle,
+      fileExt = lastDotIdx < 1 ? null : fileNoPath.substr(lastDotIdx + 1);
+
+    // if so, add it to the DOM (name-only) for use with p5.dom
+    if (validFontTypes.indexOf(fileExt) > -1) {
+
+      fontFamily = fileNoPath.substr(0, lastDotIdx);
+      newStyle = document.createElement('style');
+      newStyle.appendChild(document.createTextNode('\n@font-face {' +
+        '\nfont-family: ' + fontFamily + ';\nsrc: url(' + path + ');\n}\n'));
+      document.head.appendChild(newStyle);
+    }
+
+  });
+
+  return p5Font;
+};
+
+//BufferedReader
+p5.prototype.createInput = function () {
+  // TODO
+  throw 'not yet implemented';
+};
+
+p5.prototype.createReader = function () {
+  // TODO
+  throw 'not yet implemented';
+};
+
+p5.prototype.loadBytes = function () {
+  // TODO
+  throw 'not yet implemented';
+};
+
+/**
+ * Loads a JSON file from a file or a URL, and returns an Object or Array.
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed.
+ *
+ * @method loadJSON
+ * @param  {String}        path       name of the file or url to load
+ * @param  {Function}      [callback] function to be executed after
+ *                                    loadJSON() completes, data is passed
+ *                                    in as first argument
+ * @param  {Function}      [errorCallback] function to be executed if
+ *                                    there is an error, response is passed
+ *                                    in as first argument
+ * @param  {String}        [datatype] "json" or "jsonp"
+ * @return {Object|Array}             JSON data
+ * @example
+ *
+ * <p>Calling loadJSON() inside preload() guarantees to complete the
+ * operation before setup() and draw() are called.</p>
+ *
+ * <div><code>
+ * var weather;
+ * function preload() {
+ *   var url = 'http://api.openweathermap.org/data/2.5/weather?q=London,UK'+
+ *    '&APPID=7bbbb47522848e8b9c26ba35c226c734';
+ *   weather = loadJSON(url);
+ * }
+ *
+ * function setup() {
+ *   noLoop();
+ * }
+ *
+ * function draw() {
+ *   background(200);
+ *   // get the humidity value out of the loaded JSON
+ *   var humidity = weather.main.humidity;
+ *   fill(0, humidity); // use the humidity value to set the alpha
+ *   ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code></div>
+ *
+ *
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ * <div><code>
+ * function setup() {
+ *   noLoop();
+ *   var url = 'http://api.openweathermap.org/data/2.5/weather?q=NewYork'+
+ *    '&APPID=7bbbb47522848e8b9c26ba35c226c734';
+ *   loadJSON(url, drawWeather);
+ * }
+ *
+ * function draw() {
+ *   background(200);
+ * }
+ *
+ * function drawWeather(weather) {
+ *   // get the humidity value out of the loaded JSON
+ *   var humidity = weather.main.humidity;
+ *   fill(0, humidity); // use the humidity value to set the alpha
+ *   ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code></div>
+ *
+ */
+p5.prototype.loadJSON = function () {
+  var path = arguments[0];
+  var callback = arguments[1];
+  var errorCallback;
+  var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+  var ret = []; // array needed for preload
+  // assume jsonp for URLs
+  var t = 'json'; //= path.indexOf('http') === -1 ? 'json' : 'jsonp';
+
+  // check for explicit data type argument
+  for (var i = 2; i < arguments.length; i++) {
+    var arg = arguments[i];
+    if (typeof arg === 'string') {
+      if (arg === 'jsonp' || arg === 'json') {
+        t = arg;
+      }
+    } else if (typeof arg === 'function') {
+      errorCallback = arg;
+    }
+  }
+
+  reqwest({
+    url: path,
+    type: t,
+    crossOrigin: true,
+    error: function (resp) {
+      // pass to error callback if defined
+      if (errorCallback) {
+        errorCallback(resp);
+      } else { // otherwise log error msg
+        console.log(resp.statusText);
+      }
+    },
+    success: function (resp) {
+      for (var k in resp) {
+        ret[k] = resp[k];
+      }
+      if (typeof callback !== 'undefined') {
+        callback(resp);
+      }
+      if (decrementPreload && (callback !== decrementPreload)) {
+        decrementPreload();
+      }
+    }
+  });
+
+  return ret;
+};
+
+/**
+ * Reads the contents of a file and creates a String array of its individual
+ * lines. If the name of the file is used as the parameter, as in the above
+ * example, the file must be located in the sketch directory/folder.
+ * <br><br>
+ * Alternatively, the file maybe be loaded from anywhere on the local
+ * computer using an absolute path (something that starts with / on Unix and
+ * Linux, or a drive letter on Windows), or the filename parameter can be a
+ * URL for a file found on a network.
+ * <br><br>
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed.
+ *
+ * @method loadStrings
+ * @param  {String}   filename   name of the file or url to load
+ * @param  {Function} [callback] function to be executed after loadStrings()
+ *                               completes, Array is passed in as first
+ *                               argument
+ * @param  {Function} [errorCallback] function to be executed if
+ *                               there is an error, response is passed
+ *                               in as first argument
+ * @return {Array}               Array of Strings
+ * @example
+ *
+ * <p>Calling loadStrings() inside preload() guarantees to complete the
+ * operation before setup() and draw() are called.</p>
+ *
+ * <div><code>
+ * var result;
+ * function preload() {
+ *   result = loadStrings('assets/test.txt');
+ * }
+
+ * function setup() {
+ *   background(200);
+ *   var ind = floor(random(result.length));
+ *   text(result[ind], 10, 10, 80, 80);
+ * }
+ * </code></div>
+ *
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ *
+ * <div><code>
+ * function setup() {
+ *   loadStrings('assets/test.txt', pickString);
+ * }
+ *
+ * function pickString(result) {
+ *   background(200);
+ *   var ind = floor(random(result.length));
+ *   text(result[ind], 10, 10, 80, 80);
+ * }
+ * </code></div>
+ */
+p5.prototype.loadStrings = function (path, callback, errorCallback) {
+  var ret = [];
+  var req = new XMLHttpRequest();
+  var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+  req.addEventListener('error', function (resp) {
+    if (errorCallback) {
+      errorCallback(resp);
+    } else {
+      console.log(resp.responseText);
+    }
+  });
+
+  req.open('GET', path, true);
+  req.onreadystatechange = function () {
+    if (req.readyState === 4) {
+      if (req.status === 200) {
+        var arr = req.responseText.match(/[^\r\n]+/g);
+        for (var k in arr) {
+          ret[k] = arr[k];
+        }
+        if (typeof callback !== 'undefined') {
+          callback(ret);
+        }
+        if (decrementPreload && (callback !== decrementPreload)) {
+          decrementPreload();
+        }
+      } else {
+        if (errorCallback) {
+          errorCallback(req);
+        } else {
+          console.log(req.statusText);
+        }
+        //p5._friendlyFileLoadError(3, path);
+      }
+    }
+  };
+  req.send(null);
+  return ret;
+};
+
+/**
+ * <p>Reads the contents of a file or URL and creates a p5.Table object with
+ * its values. If a file is specified, it must be located in the sketch's
+ * "data" folder. The filename parameter can also be a URL to a file found
+ * online. By default, the file is assumed to be comma-separated (in CSV
+ * format). Table only looks for a header row if the 'header' option is
+ * included.</p>
+ *
+ * <p>Possible options include:
+ * <ul>
+ * <li>csv - parse the table as comma-separated values</li>
+ * <li>tsv - parse the table as tab-separated values</li>
+ * <li>header - this table has a header (title) row</li>
+ * </ul>
+ * </p>
+ *
+ * <p>When passing in multiple options, pass them in as separate parameters,
+ * seperated by commas. For example:
+ * <br><br>
+ * <code>
+ *   loadTable("my_csv_file.csv", "csv", "header")
+ * </code>
+ * </p>
+ *
+ * <p> All files loaded and saved use UTF-8 encoding.</p>
+ *
+ * <p>This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed. Calling loadTable() inside preload()
+ * guarantees to complete the operation before setup() and draw() are called.
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ * </p>
+ *
+ * @method loadTable
+ * @param  {String}         filename   name of the file or URL to load
+ * @param  {String|Strings} [options]  "header" "csv" "tsv"
+ * @param  {Function}       [callback] function to be executed after
+ *                                     loadTable() completes, Table object is
+ *                                     passed in as first argument
+ * @return {Object}                    Table object containing data
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the following CSV file called "mammals.csv"
+ * // located in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ *   //my table is comma separated value "csv"
+ *   //and has a header specifying the columns labels
+ *   table = loadTable("assets/mammals.csv", "csv", "header");
+ *   //the file can be remote
+ *   //table = loadTable("http://p5js.org/reference/assets/mammals.csv",
+ *   //                  "csv", "header");
+ * }
+ *
+ * function setup() {
+ *   //count the columns
+ *   print(table.getRowCount() + " total rows in table");
+ *   print(table.getColumnCount() + " total columns in table");
+ *
+ *   print(table.getColumn("name"));
+ *   //["Goat", "Leopard", "Zebra"]
+ *
+ *   //cycle through the table
+ *   for (var r = 0; r < table.getRowCount(); r++)
+ *     for (var c = 0; c < table.getColumnCount(); c++) {
+ *       print(table.getString(r, c));
+ *     }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.loadTable = function (path) {
+  var callback = null;
+  var options = [];
+  var header = false;
+  var sep = ',';
+  var separatorSet = false;
+  var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+  for (var i = 1; i < arguments.length; i++) {
+    if ((typeof (arguments[i]) === 'function') &&
+      (arguments[i] !== decrementPreload)) {
+      callback = arguments[i];
+    } else if (typeof (arguments[i]) === 'string') {
+      options.push(arguments[i]);
+      if (arguments[i] === 'header') {
+        header = true;
+      }
+      if (arguments[i] === 'csv') {
+        if (separatorSet) {
+          throw new Error('Cannot set multiple separator types.');
+        } else {
+          sep = ',';
+          separatorSet = true;
+        }
+      } else if (arguments[i] === 'tsv') {
+        if (separatorSet) {
+          throw new Error('Cannot set multiple separator types.');
+        } else {
+          sep = '\t';
+          separatorSet = true;
+        }
+      }
+    }
+  }
+
+  var t = new p5.Table();
+  reqwest({
+      url: path,
+      crossOrigin: true,
+      type: 'csv'
+    })
+    .then(function (resp) {
+      resp = resp.responseText;
+
+      var state = {};
+
+      // define constants
+      var PRE_TOKEN = 0,
+        MID_TOKEN = 1,
+        POST_TOKEN = 2,
+        POST_RECORD = 4;
+
+      var QUOTE = '\"',
+        CR = '\r',
+        LF = '\n';
+
+      var records = [];
+      var offset = 0;
+      var currentRecord = null;
+      var currentChar;
+
+      var recordBegin = function () {
+        state.escaped = false;
+        currentRecord = [];
+        tokenBegin();
+      };
+
+      var recordEnd = function () {
+        state.currentState = POST_RECORD;
+        records.push(currentRecord);
+        currentRecord = null;
+      };
+
+      var tokenBegin = function () {
+        state.currentState = PRE_TOKEN;
+        state.token = '';
+      };
+
+      var tokenEnd = function () {
+        currentRecord.push(state.token);
+        tokenBegin();
+      };
+
+      while (true) {
+        currentChar = resp[offset++];
+
+        // EOF
+        if (currentChar == null) {
+          if (state.escaped) {
+            throw new Error('Unclosed quote in file.');
+          }
+          if (currentRecord) {
+            tokenEnd();
+            recordEnd();
+            break;
+          }
+        }
+        if (currentRecord === null) {
+          recordBegin();
+        }
+
+        // Handle opening quote
+        if (state.currentState === PRE_TOKEN) {
+          if (currentChar === QUOTE) {
+            state.escaped = true;
+            state.currentState = MID_TOKEN;
+            continue;
+          }
+          state.currentState = MID_TOKEN;
+        }
+
+        // mid-token and escaped, look for sequences and end quote
+        if (state.currentState === MID_TOKEN && state.escaped) {
+          if (currentChar === QUOTE) {
+            if (resp[offset] === QUOTE) {
+              state.token += QUOTE;
+              offset++;
+            } else {
+              state.escaped = false;
+              state.currentState = POST_TOKEN;
+            }
+          } else {
+            state.token += currentChar;
+          }
+          continue;
+        }
+
+        // fall-through: mid-token or post-token, not escaped
+        if (currentChar === CR) {
+          if (resp[offset] === LF) {
+            offset++;
+          }
+          tokenEnd();
+          recordEnd();
+        } else if (currentChar === LF) {
+          tokenEnd();
+          recordEnd();
+        } else if (currentChar === sep) {
+          tokenEnd();
+        } else if (state.currentState === MID_TOKEN) {
+          state.token += currentChar;
+        }
+      }
+
+      // set up column names
+      if (header) {
+        t.columns = records.shift();
+      } else {
+        for (i = 0; i < records[0].length; i++) {
+          t.columns[i] = 'null';
+        }
+      }
+      var row;
+      for (i = 0; i < records.length; i++) {
+        //Handles row of 'undefined' at end of some CSVs
+        if (i === records.length - 1 && records[i].length === 1) {
+          if (records[i][0] === 'undefined') {
+            break;
+          }
+        }
+        row = new p5.TableRow();
+        row.arr = records[i];
+        row.obj = makeObject(records[i], t.columns);
+        t.addRow(row);
+      }
+      if (callback !== null) {
+        callback(t);
+      }
+      if (decrementPreload && (callback !== decrementPreload)) {
+        decrementPreload();
+      }
+    })
+    .fail(function (err, msg) {
+      p5._friendlyFileLoadError(2, path);
+      // don't get error callback mixed up with decrementPreload
+      if ((typeof callback !== 'undefined') &&
+        (callback !== decrementPreload)) {
+        callback(false);
+      }
+    });
+
+  return t;
+};
+
+// helper function to turn a row into a JSON object
+function makeObject(row, headers) {
+  var ret = {};
+  headers = headers || [];
+  if (typeof (headers) === 'undefined') {
+    for (var j = 0; j < row.length; j++) {
+      headers[j.toString()] = j;
+    }
+  }
+  for (var i = 0; i < headers.length; i++) {
+    var key = headers[i];
+    var val = row[i];
+    ret[key] = val;
+  }
+  return ret;
+}
+
+/**
+ * Reads the contents of a file and creates an XML object with its values.
+ * If the name of the file is used as the parameter, as in the above example,
+ * the file must be located in the sketch directory/folder.
+ * <br><br>
+ * Alternatively, the file maybe be loaded from anywhere on the local
+ * computer using an absolute path (something that starts with / on Unix and
+ * Linux, or a drive letter on Windows), or the filename parameter can be a
+ * URL for a file found on a network.
+ * <br><br>
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed. Calling loadXML() inside preload()
+ * guarantees to complete the operation before setup() and draw() are called.
+ * <br><br>
+ * Outside of preload(), you may supply a callback function to handle the
+ * object:
+ *
+ * @method loadXML
+ * @param  {String}   filename   name of the file or URL to load
+ * @param  {Function} [callback] function to be executed after loadXML()
+ *                               completes, XML object is passed in as
+ *                               first argument
+ * @param  {Function} [errorCallback] function to be executed if
+ *                               there is an error, response is passed
+ *                               in as first argument
+ * @return {Object}              XML object containing data
+ */
+p5.prototype.loadXML = function (path, callback, errorCallback) {
+  var ret = document.implementation.createDocument(null, null);
+  var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+  reqwest({
+      url: path,
+      type: 'xml',
+      crossOrigin: true,
+      error: function (resp) {
+        // pass to error callback if defined
+        if (errorCallback) {
+          errorCallback(resp);
+        } else { // otherwise log error msg
+          console.log(resp.statusText);
+        }
+        //p5._friendlyFileLoadError(1,path);
+      }
+    })
+    .then(function (resp) {
+      var x = resp.documentElement;
+      ret.appendChild(x);
+      if (typeof callback !== 'undefined') {
+        callback(ret);
+      }
+      if (decrementPreload && (callback !== decrementPreload)) {
+        decrementPreload();
+      }
+    });
+  return ret;
+};
+
+// name clash with window.open
+// p5.prototype.open = function() {
+//   // TODO
+
+// };
+
+p5.prototype.parseXML = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+p5.prototype.selectFolder = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+p5.prototype.selectInput = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+/**
+ * Method for executing an HTTP GET request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.
+ *
+ * @method httpGet
+ * @param  {String}        path       name of the file or url to load
+ * @param  {Object}        [data]     param data passed sent with request
+ * @param  {String}        [datatype] "json", "jsonp", "xml", or "text"
+ * @param  {Function}      [callback] function to be executed after
+ *                                    httpGet() completes, data is passed in
+ *                                    as first argument
+ * @param  {Function}      [errorCallback] function to be executed if
+ *                                    there is an error, response is passed
+ *                                    in as first argument
+ */
+p5.prototype.httpGet = function () {
+  var args = Array.prototype.slice.call(arguments);
+  args.push('GET');
+  p5.prototype.httpDo.apply(this, args);
+};
+
+/**
+ * Method for executing an HTTP POST request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.
+ *
+ * @method httpPost
+ * @param  {String}        path       name of the file or url to load
+ * @param  {Object}        [data]     param data passed sent with request
+ * @param  {String}        [datatype] "json", "jsonp", "xml", or "text"
+ * @param  {Function}      [callback] function to be executed after
+ *                                    httpGet() completes, data is passed in
+ *                                    as first argument
+ * @param  {Function}      [errorCallback] function to be executed if
+ *                                    there is an error, response is passed
+ *                                    in as first argument
+ */
+p5.prototype.httpPost = function () {
+  var args = Array.prototype.slice.call(arguments);
+  args.push('POST');
+  p5.prototype.httpDo.apply(this, args);
+};
+
+/**
+ * Method for executing an HTTP request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.<br><br>
+ * You may also pass a single object specifying all parameters for the
+ * request following the examples inside the reqwest() calls here:
+ * <a href='https://github.com/ded/reqwest#api'
+ * >https://github.com/ded/reqwest#api</a>
+ *
+ * @method httpDo
+ * @param  {String}        path       name of the file or url to load
+ * @param  {String}        [method]   either "GET", "POST", or "PUT",
+ *                                    defaults to "GET"
+ * @param  {Object}        [data]     param data passed sent with request
+ * @param  {String}        [datatype] "json", "jsonp", "xml", or "text"
+ * @param  {Function}      [callback] function to be executed after
+ *                                    httpGet() completes, data is passed in
+ *                                    as first argument
+ * @param  {Function}      [errorCallback] function to be executed if
+ *                                    there is an error, response is passed
+ *                                    in as first argument
+ */
+p5.prototype.httpDo = function () {
+  if (typeof arguments[0] === 'object') {
+    reqwest(arguments[0]);
+  } else {
+    var method = 'GET';
+    var path = arguments[0];
+    var data = {};
+    var type = '';
+    var callback;
+    var errorCallback;
+
+    for (var i = 1; i < arguments.length; i++) {
+      var a = arguments[i];
+      if (typeof a === 'string') {
+        if (a === 'GET' || a === 'POST' || a === 'PUT') {
+          method = a;
+        } else {
+          type = a;
+        }
+      } else if (typeof a === 'object') {
+        data = a;
+      } else if (typeof a === 'function') {
+        if (!callback) {
+          callback = a;
+        } else {
+          errorCallback = a;
+        }
+      }
+    }
+
+    // do some sort of smart type checking
+    if (type === '') {
+      if (path.indexOf('json') !== -1) {
+        type = 'json';
+      } else if (path.indexOf('xml') !== -1) {
+        type = 'xml';
+      } else {
+        type = 'text';
+      }
+    }
+
+    reqwest({
+      url: path,
+      method: method,
+      data: data,
+      type: type,
+      crossOrigin: true,
+      success: function (resp) {
+        if (typeof callback !== 'undefined') {
+          if (type === 'text') {
+            callback(resp.response);
+          } else {
+            callback(resp);
+          }
+        }
+      },
+      error: function (resp) {
+        if (errorCallback) {
+          errorCallback(resp);
+        } else {
+          console.log(resp.statusText);
+        }
+      }
+    });
+  }
+};
+
+/**
+ * @module IO
+ * @submodule Output
+ * @for p5
+ */
+
+window.URL = window.URL || window.webkitURL;
+
+// private array of p5.PrintWriter objects
+p5.prototype._pWriters = [];
+
+p5.prototype.beginRaw = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+p5.prototype.beginRecord = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+p5.prototype.createOutput = function () {
+  // TODO
+
+  throw 'not yet implemented';
+};
+
+p5.prototype.createWriter = function (name, extension) {
+  var newPW;
+  // check that it doesn't already exist
+  for (var i in p5.prototype._pWriters) {
+    if (p5.prototype._pWriters[i].name === name) {
+      // if a p5.PrintWriter w/ this name already exists...
+      // return p5.prototype._pWriters[i]; // return it w/ contents intact.
+      // or, could return a new, empty one with a unique name:
+      newPW = new p5.PrintWriter(name + window.millis(), extension);
+      p5.prototype._pWriters.push(newPW);
+      return newPW;
+    }
+  }
+  newPW = new p5.PrintWriter(name, extension);
+  p5.prototype._pWriters.push(newPW);
+  return newPW;
+};
+
+p5.prototype.endRaw = function () {
+  // TODO
+
+  throw 'not yet implemented';
+};
+
+p5.prototype.endRecord = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+p5.PrintWriter = function (filename, extension) {
+  var self = this;
+  this.name = filename;
+  this.content = '';
+  this.print = function (data) {
+    this.content += data;
+  };
+  this.println = function (data) {
+    this.content += data + '\n';
+  };
+  this.flush = function () {
+    this.content = '';
+  };
+  this.close = function () {
+    // convert String to Array for the writeFile Blob
+    var arr = [];
+    arr.push(this.content);
+    p5.prototype.writeFile(arr, filename, extension);
+    // remove from _pWriters array and delete self
+    for (var i in p5.prototype._pWriters) {
+      if (p5.prototype._pWriters[i].name === this.name) {
+        // remove from _pWriters array
+        p5.prototype._pWriters.splice(i, 1);
+      }
+    }
+    self.flush();
+    self = {};
+  };
+};
+
+p5.prototype.saveBytes = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+// object, filename, options --> saveJSON, saveStrings, saveTable
+// filename, [extension] [canvas] --> saveImage
+
+/**
+ *  <p>Save an image, text, json, csv, wav, or html. Prompts download to
+ *  the client's computer. <b>Note that it is not recommended to call save()
+ *  within draw if it's looping, as the save() function will open a new save
+ *  dialog every frame.</b></p>
+ *  <p>The default behavior is to save the canvas as an image. You can
+ *  optionally specify a filename.
+ *  For example:</p>
+ *  <pre class='language-javascript'><code>
+ *  save();
+ *  save('myCanvas.jpg'); // save a specific canvas with a filename
+ *  </code></pre>
+ *
+ *  <p>Alternately, the first parameter can be a pointer to a canvas
+ *  p5.Element, an Array of Strings,
+ *  an Array of JSON, a JSON object, a p5.Table, a p5.Image, or a
+ *  p5.SoundFile (requires p5.sound). The second parameter is a filename
+ *  (including extension). The third parameter is for options specific
+ *  to this type of object. This method will save a file that fits the
+ *  given paramaters. For example:</p>
+ *
+ *  <pre class='language-javascript'><code>
+ *
+ *  save('myCanvas.jpg');           // Saves canvas as an image
+ *
+ *  var cnv = createCanvas(100, 100);
+ *  save(cnv, 'myCanvas.jpg');      // Saves canvas as an image
+ *
+ *  var gb = createGraphics(100, 100);
+ *  save(gb, 'myGraphics.jpg');      // Saves p5.Renderer object as an image
+ *
+ *  save(myTable, 'myTable.html');  // Saves table as html file
+ *  save(myTable, 'myTable.csv',);  // Comma Separated Values
+ *  save(myTable, 'myTable.tsv');   // Tab Separated Values
+ *
+ *  save(myJSON, 'my.json');        // Saves pretty JSON
+ *  save(myJSON, 'my.json', true);  // Optimizes JSON filesize
+ *
+ *  save(img, 'my.png');            // Saves pImage as a png image
+ *
+ *  save(arrayOfStrings, 'my.txt'); // Saves strings to a text file with line
+ *                                  // breaks after each item in the array
+ *  </code></pre>
+ *
+ *  @method save
+ *  @param  {[Object|String]} objectOrFilename  If filename is provided, will
+ *                                             save canvas as an image with
+ *                                             either png or jpg extension
+ *                                             depending on the filename.
+ *                                             If object is provided, will
+ *                                             save depending on the object
+ *                                             and filename (see examples
+ *                                             above).
+ *  @param  {[String]} filename If an object is provided as the first
+ *                               parameter, then the second parameter
+ *                               indicates the filename,
+ *                               and should include an appropriate
+ *                               file extension (see examples above).
+ *  @param  {[Boolean/String]} options  Additional options depend on
+ *                            filetype. For example, when saving JSON,
+ *                            <code>true</code> indicates that the
+ *                            output will be optimized for filesize,
+ *                            rather than readability.
+ */
+p5.prototype.save = function (object, _filename, _options) {
+  // parse the arguments and figure out which things we are saving
+  var args = arguments;
+  // =================================================
+  // OPTION 1: saveCanvas...
+
+  // if no arguments are provided, save canvas
+  var cnv = this._curElement.elt;
+  if (args.length === 0) {
+    p5.prototype.saveCanvas(cnv);
+    return;
+  }
+  // otherwise, parse the arguments
+
+  // if first param is a p5Graphics, then saveCanvas
+  else if (args[0] instanceof p5.Renderer ||
+    args[0] instanceof p5.Graphics) {
+    p5.prototype.saveCanvas(args[0].elt, args[1], args[2]);
+    return;
+  }
+
+  // if 1st param is String and only one arg, assume it is canvas filename
+  else if (args.length === 1 && typeof (args[0]) === 'string') {
+    p5.prototype.saveCanvas(cnv, args[0]);
+  }
+
+  // =================================================
+  // OPTION 2: extension clarifies saveStrings vs. saveJSON
+  else {
+    var extension = _checkFileExtension(args[1], args[2])[1];
+    switch (extension) {
+    case 'json':
+      p5.prototype.saveJSON(args[0], args[1], args[2]);
+      return;
+    case 'txt':
+      p5.prototype.saveStrings(args[0], args[1], args[2]);
+      return;
+      // =================================================
+      // OPTION 3: decide based on object...
+    default:
+      if (args[0] instanceof Array) {
+        p5.prototype.saveStrings(args[0], args[1], args[2]);
+      } else if (args[0] instanceof p5.Table) {
+        p5.prototype.saveTable(args[0], args[1], args[2], args[3]);
+      } else if (args[0] instanceof p5.Image) {
+        p5.prototype.saveCanvas(args[0].canvas, args[1]);
+      } else if (args[0] instanceof p5.SoundFile) {
+        p5.prototype.saveSound(args[0], args[1], args[2], args[3]);
+      }
+    }
+  }
+};
+
+/**
+ *  Writes the contents of an Array or a JSON object to a .json file.
+ *  The file saving process and location of the saved file will
+ *  vary between web browsers.
+ *
+ *  @method saveJSON
+ *  @param  {Array|Object} json
+ *  @param  {String} filename
+ *  @param  {Boolean} [optimize]   If true, removes line breaks
+ *                                 and spaces from the output
+ *                                 file to optimize filesize
+ *                                 (but not readability).
+ *  @example
+ *  <div><code>
+ *  var json;
+ *
+ *  function setup() {
+ *
+ *    json = {}; // new JSON Object
+ *
+ *    json.id = 0;
+ *    json.species = 'Panthera leo';
+ *    json.name = 'Lion';
+ *
+ *  // To save, un-comment the line below, then click 'run'
+ *  // saveJSONObject(json, 'lion.json');
+ *  }
+ *
+ *  // Saves the following to a file called "lion.json":
+ *  // {
+ *  //   "id": 0,
+ *  //   "species": "Panthera leo",
+ *  //   "name": "Lion"
+ *  // }
+ *  </div></code>
+ */
+p5.prototype.saveJSON = function (json, filename, opt) {
+  var stringify;
+  if (opt) {
+    stringify = JSON.stringify(json);
+  } else {
+    stringify = JSON.stringify(json, undefined, 2);
+  }
+  console.log(stringify);
+  this.saveStrings(stringify.split('\n'), filename, 'json');
+};
+
+p5.prototype.saveJSONObject = p5.prototype.saveJSON;
+p5.prototype.saveJSONArray = p5.prototype.saveJSON;
+
+p5.prototype.saveStream = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+/**
+ *  Writes an array of Strings to a text file, one line per String.
+ *  The file saving process and location of the saved file will
+ *  vary between web browsers.
+ *
+ *  @method saveStrings
+ *  @param  {Array} list      string array to be written
+ *  @param  {String} filename filename for output
+ *  @example
+ *  <div><code>
+ *  var words = 'apple bear cat dog';
+ *
+ *  // .split() outputs an Array
+ *  var list = split(words, ' ');
+ *
+ *  // To save the file, un-comment next line and click 'run'
+ *  // saveStrings(list, 'nouns.txt');
+ *
+ *  // Saves the following to a file called 'nouns.txt':
+ *  //
+ *  // apple
+ *  // bear
+ *  // cat
+ *  // dog
+ *  </code></div>
+ */
+p5.prototype.saveStrings = function (list, filename, extension) {
+  var ext = extension || 'txt';
+  var pWriter = this.createWriter(filename, ext);
+  for (var i = 0; i < list.length; i++) {
+    if (i < list.length - 1) {
+      pWriter.println(list[i]);
+    } else {
+      pWriter.print(list[i]);
+    }
+  }
+  pWriter.close();
+  pWriter.flush();
+};
+
+p5.prototype.saveXML = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+p5.prototype.selectOutput = function () {
+  // TODO
+  throw 'not yet implemented';
+
+};
+
+// =======
+// HELPERS
+// =======
+
+function escapeHelper(content) {
+  return content
+    .replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#039;');
+}
+
+/**
+ *  Writes the contents of a Table object to a file. Defaults to a
+ *  text file with comma-separated-values ('csv') but can also
+ *  use tab separation ('tsv'), or generate an HTML table ('html').
+ *  The file saving process and location of the saved file will
+ *  vary between web browsers.
+ *
+ *  @method saveTable
+ *  @param  {p5.Table} Table  the Table object to save to a file
+ *  @param  {String} filename the filename to which the Table should be saved
+ *  @param  {String} [options]  can be one of "tsv", "csv", or "html"
+ *  @example
+ *  <div><code>
+ *  var table;
+ *
+ *  function setup() {
+ *    table = new p5.Table();
+ *
+ *    table.addColumn('id');
+ *    table.addColumn('species');
+ *    table.addColumn('name');
+ *
+ *    var newRow = table.addRow();
+ *    newRow.setNum('id', table.getRowCount() - 1);
+ *    newRow.setString('species', 'Panthera leo');
+ *    newRow.setString('name', 'Lion');
+ *
+ *    // To save, un-comment next line then click 'run'
+ *    // saveTable(table, 'new.csv');
+ *    }
+ *
+ *    // Saves the following to a file called 'new.csv':
+ *    // id,species,name
+ *    // 0,Panthera leo,Lion
+ *  </code></div>
+ */
+p5.prototype.saveTable = function (table, filename, options) {
+  var pWriter = this.createWriter(filename, options);
+
+  var header = table.columns;
+
+  var sep = ','; // default to CSV
+  if (options === 'tsv') {
+    sep = '\t';
+  }
+  if (options !== 'html') {
+    // make header if it has values
+    if (header[0] !== '0') {
+      for (var h = 0; h < header.length; h++) {
+        if (h < header.length - 1) {
+          pWriter.print(header[h] + sep);
+        } else {
+          pWriter.println(header[h]);
+        }
+      }
+    }
+
+    // make rows
+    for (var i = 0; i < table.rows.length; i++) {
+      var j;
+      for (j = 0; j < table.rows[i].arr.length; j++) {
+        if (j < table.rows[i].arr.length - 1) {
+          pWriter.print(table.rows[i].arr[j] + sep);
+        } else if (i < table.rows.length - 1) {
+          pWriter.println(table.rows[i].arr[j]);
+        } else {
+          pWriter.print(table.rows[i].arr[j]); // no line break
+        }
+      }
+    }
+  }
+
+  // otherwise, make HTML
+  else {
+    pWriter.println('<html>');
+    pWriter.println('<head>');
+    var str = '  <meta http-equiv=\"content-type\" content';
+    str += '=\"text/html;charset=utf-8\" />';
+    pWriter.println(str);
+    pWriter.println('</head>');
+
+    pWriter.println('<body>');
+    pWriter.println('  <table>');
+
+    // make header if it has values
+    if (header[0] !== '0') {
+      pWriter.println('    <tr>');
+      for (var k = 0; k < header.length; k++) {
+        var e = escapeHelper(header[k]);
+        pWriter.println('      <td>' + e);
+        pWriter.println('      </td>');
+      }
+      pWriter.println('    </tr>');
+    }
+
+    // make rows
+    for (var row = 0; row < table.rows.length; row++) {
+      pWriter.println('    <tr>');
+      for (var col = 0; col < table.columns.length; col++) {
+        var entry = table.rows[row].getString(col);
+        var htmlEntry = escapeHelper(entry);
+        pWriter.println('      <td>' + htmlEntry);
+        pWriter.println('      </td>');
+      }
+      pWriter.println('    </tr>');
+    }
+    pWriter.println('  </table>');
+    pWriter.println('</body>');
+    pWriter.print('</html>');
+  }
+  // close and flush the pWriter
+  pWriter.close();
+  pWriter.flush();
+}; // end saveTable()
+
+/**
+ *  Generate a blob of file data as a url to prepare for download.
+ *  Accepts an array of data, a filename, and an extension (optional).
+ *  This is a private function because it does not do any formatting,
+ *  but it is used by saveStrings, saveJSON, saveTable etc.
+ *
+ *  @param  {Array} dataToDownload
+ *  @param  {String} filename
+ *  @param  {[String]} extension
+ *  @private
+ */
+p5.prototype.writeFile = function (dataToDownload, filename, extension) {
+  var type = 'application\/octet-stream';
+  if (p5.prototype._isSafari()) {
+    type = 'text\/plain';
+  }
+  var blob = new Blob(dataToDownload, {
+    'type': type
+  });
+  var href = window.URL.createObjectURL(blob);
+  p5.prototype.downloadFile(href, filename, extension);
+};
+
+/**
+ *  Forces download. Accepts a url to filedata/blob, a filename,
+ *  and an extension (optional).
+ *  This is a private function because it does not do any formatting,
+ *  but it is used by saveStrings, saveJSON, saveTable etc.
+ *
+ *  @param  {String} href      i.e. an href generated by createObjectURL
+ *  @param  {[String]} filename
+ *  @param  {[String]} extension
+ */
+p5.prototype.downloadFile = function (href, fName, extension) {
+  var fx = _checkFileExtension(fName, extension);
+  var filename = fx[0];
+  var ext = fx[1];
+
+  var a = document.createElement('a');
+  a.href = href;
+  a.download = filename;
+
+  // Firefox requires the link to be added to the DOM before click()
+  a.onclick = destroyClickedElement;
+  a.style.display = 'none';
+  document.body.appendChild(a);
+
+  // Safari will open this file in the same page as a confusing Blob.
+  if (p5.prototype._isSafari()) {
+    var aText = 'Hello, Safari user! To download this file...\n';
+    aText += '1. Go to File --> Save As.\n';
+    aText += '2. Choose "Page Source" as the Format.\n';
+    aText += '3. Name it with this extension: .\"' + ext + '\"';
+    alert(aText);
+  }
+  a.click();
+  href = null;
+};
+
+/**
+ *  Returns a file extension, or another string
+ *  if the provided parameter has no extension.
+ *
+ *  @param   {String} filename
+ *  @return  {Array} [fileName, fileExtension]
+ *
+ *  @private
+ */
+function _checkFileExtension(filename, extension) {
+  if (!extension || extension === true || extension === 'true') {
+    extension = '';
+  }
+  if (!filename) {
+    filename = 'untitled';
+  }
+  var ext = '';
+  // make sure the file will have a name, see if filename needs extension
+  if (filename && filename.indexOf('.') > -1) {
+    ext = filename.split('.').pop();
+  }
+  // append extension if it doesn't exist
+  if (extension) {
+    if (ext !== extension) {
+      ext = extension;
+      filename = filename + '.' + ext;
+    }
+  }
+  return [filename, ext];
+}
+p5.prototype._checkFileExtension = _checkFileExtension;
+
+/**
+ *  Returns true if the browser is Safari, false if not.
+ *  Safari makes trouble for downloading files.
+ *
+ *  @return  {Boolean} [description]
+ *  @private
+ */
+p5.prototype._isSafari = function () {
+  var x = Object.prototype.toString.call(window.HTMLElement);
+  return x.indexOf('Constructor') > 0;
+};
+
+/**
+ *  Helper function, a callback for download that deletes
+ *  an invisible anchor element from the DOM once the file
+ *  has been automatically downloaded.
+ *
+ *  @private
+ */
+function destroyClickedElement(event) {
+  document.body.removeChild(event.target);
+}
+
+module.exports = p5;
+
+},{"../core/core":48,"../core/error_helpers":51,"opentype.js":8,"reqwest":27}],71:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Table
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+
+/**
+ *  Table Options
+ *  <p>Generic class for handling tabular data, typically from a
+ *  CSV, TSV, or other sort of spreadsheet file.</p>
+ *  <p>CSV files are
+ *  <a href="http://en.wikipedia.org/wiki/Comma-separated_values">
+ *  comma separated values</a>, often with the data in quotes. TSV
+ *  files use tabs as separators, and usually don't bother with the
+ *  quotes.</p>
+ *  <p>File names should end with .csv if they're comma separated.</p>
+ *  <p>A rough "spec" for CSV can be found
+ *  <a href="http://tools.ietf.org/html/rfc4180">here</a>.</p>
+ *  <p>To load files, use the loadTable method.</p>
+ *  <p>To save tables to your computer, use the save method
+ *   or the saveTable method.</p>
+ *
+ *  Possible options include:
+ *  <ul>
+ *  <li>csv - parse the table as comma-separated values
+ *  <li>tsv - parse the table as tab-separated values
+ *  <li>header - this table has a header (title) row
+ *  </ul>
+ */
+
+/**
+ *  Table objects store data with multiple rows and columns, much
+ *  like in a traditional spreadsheet. Tables can be generated from
+ *  scratch, dynamically, or using data from an existing file.
+ *
+ *  @class p5.Table
+ *  @constructor
+ *  @param  {Array}     [rows] An array of p5.TableRow objects
+ *  @return {p5.Table}         p5.Table generated
+ */
+p5.Table = function (rows) {
+  /**
+   *  @property columns
+   *  @type {Array}
+   */
+  this.columns = [];
+
+  /**
+   *  @property rows
+   *  @type {Array}
+   */
+  this.rows = [];
+};
+
+/**
+ *  Use addRow() to add a new row of data to a p5.Table object. By default,
+ *  an empty row is created. Typically, you would store a reference to
+ *  the new row in a TableRow object (see newRow in the example above),
+ *  and then set individual values using set().
+ *
+ *  If a p5.TableRow object is included as a parameter, then that row is
+ *  duplicated and added to the table.
+ *
+ *  @method  addRow
+ *  @param   {p5.TableRow} [row] row to be added to the table
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   //add a row
+	*   var newRow = table.addRow();
+	*   newRow.setString("id", table.getRowCount() - 1);
+	*   newRow.setString("species", "Canis Lupus");
+	*   newRow.setString("name", "Wolf");
+	*
+	*   //print the results
+	*   for (var r = 0; r < table.getRowCount(); r++)
+	*     for (var c = 0; c < table.getColumnCount(); c++)
+	*       print(table.getString(r, c));
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.addRow = function(row) {
+  // make sure it is a valid TableRow
+  var r = row || new p5.TableRow();
+
+  if (typeof(r.arr) === 'undefined' || typeof(r.obj) === 'undefined') {
+    //r = new p5.prototype.TableRow(r);
+    throw 'invalid TableRow: ' + r;
+  }
+  r.table = this;
+  this.rows.push(r);
+  return r;
+};
+
+/**
+ * Removes a row from the table object.
+ *
+ * @method  removeRow
+ * @param   {Number} id ID number of the row to remove
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   //remove the first row
+	*   var r = table.removeRow(0);
+	*
+	*   //print the results
+	*   for (var r = 0; r < table.getRowCount(); r++)
+	*     for (var c = 0; c < table.getColumnCount(); c++)
+	*       print(table.getString(r, c));
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.removeRow = function(id) {
+  this.rows[id].table = null; // remove reference to table
+  var chunk = this.rows.splice(id+1, this.rows.length);
+  this.rows.pop();
+  this.rows = this.rows.concat(chunk);
+};
+
+
+/**
+ * Returns a reference to the specified p5.TableRow. The reference
+ * can then be used to get and set values of the selected row.
+ *
+ * @method  getRow
+ * @param  {Number}   rowID ID number of the row to get
+ * @return {TableRow} p5.TableRow object
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   var row = table.getRow(1);
+	*   //print it column by column
+	*   //note: a row is an object, not an array
+	*   for (var c = 0; c < table.getColumnCount(); c++)
+	*     print(row.getString(c));
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.getRow = function(r) {
+  return this.rows[r];
+};
+
+/**
+ *  Gets all rows from the table. Returns an array of p5.TableRows.
+ *
+ *  @method  getRows
+ *  @return {Array}   Array of p5.TableRows
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   var rows = table.getRows();
+	*
+	*   //warning: rows is an array of objects
+	*   for (var r = 0; r < rows.length; r++)
+	*     rows[r].set("name", "Unicorn");
+	*
+	*   //print the results
+	*   for (var r = 0; r < table.getRowCount(); r++)
+	*     for (var c = 0; c < table.getColumnCount(); c++)
+	*       print(table.getString(r, c));
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.getRows = function() {
+  return this.rows;
+};
+
+/**
+ *  Finds the first row in the Table that contains the value
+ *  provided, and returns a reference to that row. Even if
+ *  multiple rows are possible matches, only the first matching
+ *  row is returned. The column to search may be specified by
+ *  either its ID or title.
+ *
+ *  @method  findRow
+ *  @param  {String} value  The value to match
+ *  @param  {Number|String} column ID number or title of the
+ *                                 column to search
+ *  @return {TableRow}
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   //find the animal named zebra
+	*   var row = table.findRow("Zebra", "name");
+	*   //find the corresponding species
+	*   print(row.getString("species"));
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.findRow = function(value, column) {
+  // try the Object
+  if (typeof(column) === 'string') {
+    for (var i = 0; i < this.rows.length; i++){
+      if (this.rows[i].obj[column] === value) {
+        return this.rows[i];
+      }
+    }
+  }
+  // try the Array
+  else {
+    for (var j = 0; j < this.rows.length; j++){
+      if (this.rows[j].arr[column] === value) {
+        return this.rows[j];
+      }
+    }
+  }
+  // otherwise...
+  return null;
+};
+
+/**
+ *  Finds the rows in the Table that contain the value
+ *  provided, and returns references to those rows. Returns an
+ *  Array, so for must be used to iterate through all the rows,
+ *  as shown in the example above. The column to search may be
+ *  specified by either its ID or title.
+ *
+ *  @method  findRows
+ *  @param  {String} value  The value to match
+ *  @param  {Number|String} column ID number or title of the
+ *                                 column to search
+ *  @return {Array}        An Array of TableRow objects
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   //add another goat
+	*   var newRow = table.addRow();
+	*   newRow.setString("id", table.getRowCount() - 1);
+	*   newRow.setString("species", "Scape Goat");
+	*   newRow.setString("name", "Goat");
+	*
+	*   //find the rows containing animals named Goat
+	*   var rows = table.findRows("Goat", "name");
+	*   print(rows.length + " Goats found");
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.findRows = function(value, column) {
+  var ret = [];
+  if (typeof(column) === 'string') {
+    for (var i = 0; i < this.rows.length; i++){
+      if (this.rows[i].obj[column] === value) {
+        ret.push( this.rows[i] );
+      }
+    }
+  }
+  // try the Array
+  else {
+    for (var j = 0; j < this.rows.length; j++){
+      if (this.rows[j].arr[column] === value) {
+        ret.push( this.rows[j] );
+      }
+    }
+  }
+  return ret;
+};
+
+/**
+ *  Finds the first row in the Table that matches the regular
+ *  expression provided, and returns a reference to that row.
+ *  Even if multiple rows are possible matches, only the first
+ *  matching row is returned. The column to search may be
+ *  specified by either its ID or title.
+ *
+ *  @method  matchRow
+ *  @param  {String} regexp The regular expression to match
+ *  @param  {String|Number} column The column ID (number) or
+ *                                   title (string)
+ *  @return {TableRow}        TableRow object
+ */
+p5.Table.prototype.matchRow = function(regexp, column) {
+  if (typeof(column) === 'number') {
+    for (var j = 0; j < this.rows.length; j++) {
+      if ( this.rows[j].arr[column].match(regexp) ) {
+        return this.rows[j];
+      }
+    }
+  }
+
+  else {
+    for (var i = 0; i < this.rows.length; i++) {
+      if ( this.rows[i].obj[column].match(regexp) ) {
+        return this.rows[i];
+      }
+    }
+  }
+  return null;
+};
+
+/**
+ *  Finds the first row in the Table that matches the regular
+ *  expression provided, and returns a reference to that row.
+ *  Even if multiple rows are possible matches, only the first
+ *  matching row is returned. The column to search may be specified
+ *  by either its ID or title.
+ *
+ *  @method  matchRows
+ *  @param  {String} regexp The regular expression to match
+ *  @param  {String|Number} [column] The column ID (number) or
+ *                                   title (string)
+ *  @return {Array}        An Array of TableRow objects
+ */
+p5.Table.prototype.matchRows = function(regexp, column) {
+  var ret = [];
+  if (typeof(column) === 'number') {
+    for (var j = 0; j < this.rows.length; j++) {
+      if ( this.rows[j].arr[column].match(regexp) ) {
+        ret.push( this.rows[j] );
+      }
+    }
+  }
+
+  else {
+    for (var i = 0; i < this.rows.length; i++) {
+      if ( this.rows[i].obj[column].match(regexp) ) {
+        ret.push( this.rows[i] );
+      }
+    }
+  }
+  return ret;
+};
+
+
+/**
+ *  Retrieves all values in the specified column, and returns them
+ *  as an array. The column may be specified by either its ID or title.
+ *
+ *  @method  getColumn
+ *  @param  {String|Number} column String or Number of the column to return
+ *  @return {Array}       Array of column values
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   //getColumn returns an array that can be printed directly
+	*   print(table.getColumn("species"));
+	*   //outputs ["Capra hircus", "Panthera pardus", "Equus zebra"]
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.getColumn = function(value) {
+  var ret = [];
+  if (typeof(value) === 'string'){
+    for (var i = 0; i < this.rows.length; i++){
+      ret.push (this.rows[i].obj[value]);
+    }
+  } else {
+    for (var j = 0; j < this.rows.length; j++){
+      ret.push (this.rows[j].arr[value]);
+    }
+  }
+  return ret;
+};
+
+/**
+ *  Removes all rows from a Table. While all rows are removed,
+ *  columns and column titles are maintained.
+ *
+ *  @method  clearRows
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   table.clearRows();
+	*   print(table.getRowCount() + " total rows in table");
+	*   print(table.getColumnCount() + " total columns in table");
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.clearRows = function() {
+  delete this.rows;
+  this.rows = [];
+};
+
+/**
+ *  Use addColumn() to add a new column to a Table object.
+ *  Typically, you will want to specify a title, so the column
+ *  may be easily referenced later by name. (If no title is
+ *  specified, the new column's title will be null.)
+ *
+ *  @method  addColumn
+ *  @param {String} [title] title of the given column
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   table.addColumn("carnivore");
+	*   table.set(0, "carnivore", "no");
+	*   table.set(1, "carnivore", "yes");
+	*   table.set(2, "carnivore", "no");
+	*
+	*   //print the results
+	*   for (var r = 0; r < table.getRowCount(); r++)
+	*     for (var c = 0; c < table.getColumnCount(); c++)
+	*       print(table.getString(r, c));
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.addColumn = function(title) {
+  var t = title || null;
+  this.columns.push(t);
+};
+
+/**
+ *  Returns the total number of columns in a Table.
+ *
+ *  @return {Number} Number of columns in this table
+ */
+p5.Table.prototype.getColumnCount = function() {
+  return this.columns.length;
+};
+
+/**
+ *  Returns the total number of rows in a Table.
+ *
+ *  @method  getRowCount
+ *  @return {Number} Number of rows in this table
+
+ */
+p5.Table.prototype.getRowCount = function() {
+  return this.rows.length;
+};
+
+/**
+ *  <p>Removes any of the specified characters (or "tokens").</p>
+ *
+ *  <p>If no column is specified, then the values in all columns and
+ *  rows are processed. A specific column may be referenced by
+ *  either its ID or title.</p>
+ *
+ *  @method  removeTokens
+ *  @param  {String} chars  String listing characters to be removed
+ *  @param  {String|Number} [column] Column ID (number)
+ *                                   or name (string)
+ */
+p5.Table.prototype.removeTokens = function(chars, column) {
+  var escape= function(s) {
+    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+  };
+  var charArray = [];
+  for (var i = 0; i < chars.length; i++) {
+    charArray.push( escape( chars.charAt(i) ) );
+  }
+  var regex = new RegExp(charArray.join('|'), 'g');
+
+  if (typeof(column) === 'undefined'){
+    for (var c = 0; c < this.columns.length; c++) {
+      for (var d = 0; d < this.rows.length; d++) {
+        var s = this.rows[d].arr[c];
+        s = s.replace(regex, '');
+        this.rows[d].arr[c] = s;
+        this.rows[d].obj[this.columns[c]] = s;
+      }
+    }
+  }
+  else if (typeof(column) === 'string'){
+    for (var j = 0; j < this.rows.length; j++) {
+      var val = this.rows[j].obj[column];
+      val = val.replace(regex, '');
+      this.rows[j].obj[column] = val;
+      var pos = this.columns.indexOf(column);
+      this.rows[j].arr[pos] = val;
+    }
+  }
+  else {
+    for (var k = 0; k < this.rows.length; k++) {
+      var str = this.rows[k].arr[column];
+      str = str.replace(regex, '');
+      this.rows[k].arr[column] = str;
+      this.rows[k].obj[this.columns[column]] = str;
+    }
+  }
+};
+
+/**
+ *  Trims leading and trailing whitespace, such as spaces and tabs,
+ *  from String table values. If no column is specified, then the
+ *  values in all columns and rows are trimmed. A specific column
+ *  may be referenced by either its ID or title.
+ *
+ *  @method  trim
+ *  @param  {String|Number} column Column ID (number)
+ *                                   or name (string)
+ */
+p5.Table.prototype.trim = function(column) {
+  var regex = new RegExp( (' '), 'g');
+
+  if (typeof(column) === 'undefined'){
+    for (var c = 0; c < this.columns.length; c++) {
+      for (var d = 0; d < this.rows.length; d++) {
+        var s = this.rows[d].arr[c];
+        s = s.replace(regex, '');
+        this.rows[d].arr[c] = s;
+        this.rows[d].obj[this.columns[c]] = s;
+      }
+    }
+  }
+  else if (typeof(column) === 'string'){
+    for (var j = 0; j < this.rows.length; j++) {
+      var val = this.rows[j].obj[column];
+      val = val.replace(regex, '');
+      this.rows[j].obj[column] = val;
+      var pos = this.columns.indexOf(column);
+      this.rows[j].arr[pos] = val;
+    }
+  }
+  else {
+    for (var k = 0; k < this.rows.length; k++) {
+      var str = this.rows[k].arr[column];
+      str = str.replace(regex, '');
+      this.rows[k].arr[column] = str;
+      this.rows[k].obj[this.columns[column]] = str;
+    }
+  }
+};
+
+/**
+ *  Use removeColumn() to remove an existing column from a Table
+ *  object. The column to be removed may be identified by either
+ *  its title (a String) or its index value (an int).
+ *  removeColumn(0) would remove the first column, removeColumn(1)
+ *  would remove the second column, and so on.
+ *
+ *  @method  removeColumn
+ *  @param  {String|Number} column columnName (string) or ID (number)
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   table.removeColumn("id");
+	*   print(table.getColumnCount());
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.removeColumn = function(c) {
+  var cString;
+  var cNumber;
+  if (typeof(c) === 'string') {
+    // find the position of c in the columns
+    cString = c;
+    cNumber = this.columns.indexOf(c);
+    console.log('string');
+  }
+  else{
+    cNumber = c;
+    cString = this.columns[c];
+  }
+
+  var chunk = this.columns.splice(cNumber+1, this.columns.length);
+  this.columns.pop();
+  this.columns = this.columns.concat(chunk);
+
+  for (var i = 0; i < this.rows.length; i++){
+    var tempR = this.rows[i].arr;
+    var chip = tempR.splice(cNumber+1, tempR.length);
+    tempR.pop();
+    this.rows[i].arr = tempR.concat(chip);
+    delete this.rows[i].obj[cString];
+  }
+
+};
+
+
+/**
+ * Stores a value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method  set
+ * @param {String|Number} column column ID (Number)
+ *                               or title (String)
+ * @param {String|Number} value  value to assign
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   table.set(0, "species", "Canis Lupus");
+	*   table.set(0, "name", "Wolf");
+	*
+	*   //print the results
+	*   for (var r = 0; r < table.getRowCount(); r++)
+	*     for (var c = 0; c < table.getColumnCount(); c++)
+	*       print(table.getString(r, c));
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.set = function(row, column, value) {
+  this.rows[row].set(column, value);
+};
+
+/**
+ * Stores a Float value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method setNum
+ * @param {Number} row row ID
+ * @param {String|Number} column column ID (Number)
+ *                               or title (String)
+ * @param {Number} value  value to assign
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   table.setNum(1, "id", 1);
+	*
+	*   print(table.getColumn(0));
+	*   //["0", 1, "2"]
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.setNum = function(row, column, value){
+  this.rows[row].setNum(column, value);
+};
+
+
+/**
+ * Stores a String value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method  setString
+ * @param {Number} row row ID
+ * @param {String|Number} column column ID (Number)
+ *                               or title (String)
+ * @param {String} value  value to assign
+ */
+p5.Table.prototype.setString = function(row, column, value){
+  this.rows[row].setString(column, value);
+};
+
+/**
+ * Retrieves a value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method  get
+ * @param {Number} row row ID
+ * @param  {String|Number} column columnName (string) or
+ *                                   ID (number)
+ * @return {String|Number}
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   print(table.get(0, 1));
+	*   //Capra hircus
+	*   print(table.get(0, "species"));
+	*   //Capra hircus
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.get = function(row, column) {
+  return this.rows[row].get(column);
+};
+
+/**
+ * Retrieves a Float value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method  getNum
+ * @param {Number} row row ID
+ * @param  {String|Number} column columnName (string) or
+ *                                   ID (number)
+ * @return {Number}
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   print(table.getNum(1, 0) + 100);
+	*   //id 1 + 100 = 101
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.getNum = function(row, column) {
+  return this.rows[row].getNum(column);
+};
+
+/**
+ * Retrieves a String value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method  getString
+ * @param {Number} row row ID
+ * @param  {String|Number} column columnName (string) or
+ *                                   ID (number)
+ * @return {String}
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   var tableArray = table.getArray();
+	*
+	*   //output each row as array
+	*   for (var i = 0; i < tableArray.length; i++)
+	*     print(tableArray[i]);
+	* }
+	* </code>
+	* </div>
+ */
+p5.Table.prototype.getString = function(row, column) {
+  return this.rows[row].getString(column);
+};
+
+/**
+ * Retrieves all table data and returns as an object. If a column name is
+ * passed in, each row object will be stored with that attribute as its
+ * title.
+ *
+ * @method  getObject
+ * @param {String} headerColumn Name of the column which should be used to
+ *                              title each row object (optional)
+ * @return {Object}
+ *
+ * @example
+	* <div class="norender">
+	* <code>
+	* // Given the CSV file "mammals.csv"
+	* // in the project's "assets" folder:
+	* //
+	* // id,species,name
+	* // 0,Capra hircus,Goat
+	* // 1,Panthera pardus,Leopard
+	* // 2,Equus zebra,Zebra
+	*
+	* var table;
+	*
+	* function preload() {
+	*   //my table is comma separated value "csv"
+	*   //and has a header specifying the columns labels
+	*   table = loadTable("assets/mammals.csv", "csv", "header");
+	* }
+	*
+	* function setup() {
+	*   var tableObject = table.getObject();
+	*
+	*   print(tableObject);
+	*   //outputs an object
+	* }
+	* </code>
+	* </div>
+
+ */
+p5.Table.prototype.getObject = function (headerColumn) {
+  var tableObject = {};
+  var obj, cPos, index;
+
+  for(var i = 0; i < this.rows.length; i++) {
+    obj = this.rows[i].obj;
+
+    if (typeof(headerColumn) === 'string'){
+      cPos = this.columns.indexOf(headerColumn); // index of columnID
+      if (cPos >= 0) {
+        index = obj[headerColumn];
+        tableObject[index] = obj;
+      } else {
+        throw 'This table has no column named "' + headerColumn +'"';
+      }
+    } else {
+      tableObject[i] = this.rows[i].obj;
+    }
+  }
+  return tableObject;
+};
+
+/**
+ * Retrieves all table data and returns it as a multidimensional array.
+ *
+ * @method  getArray
+ * @return {Array}
+ */
+p5.Table.prototype.getArray = function () {
+  var tableArray = [];
+  for(var i = 0; i < this.rows.length; i++) {
+    tableArray.push(this.rows[i].arr);
+  }
+  return tableArray;
+};
+
+module.exports = p5.Table;
+
+},{"../core/core":48}],72:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Table
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ *  A TableRow object represents a single row of data values,
+ *  stored in columns, from a table.
+ *
+ *  A Table Row contains both an ordered array, and an unordered
+ *  JSON object.
+ *
+ *  @class p5.TableRow
+ *  @constructor
+ *  @param {String} [str]       optional: populate the row with a
+ *                              string of values, separated by the
+ *                              separator
+ *  @param {String} [separator] comma separated values (csv) by default
+ */
+p5.TableRow = function (str, separator) {
+  var arr = [];
+  var obj = {};
+  if (str){
+    separator = separator || ',';
+    arr = str.split(separator);
+  }
+  for (var i = 0; i < arr.length; i++){
+    var key = i;
+    var val = arr[i];
+    obj[key] = val;
+  }
+  this.arr = arr;
+  this.obj = obj;
+  this.table = null;
+};
+
+/**
+ *  Stores a value in the TableRow's specified column.
+ *  The column may be specified by either its ID or title.
+ *
+ *  @method  set
+ *  @param {String|Number} column Column ID (Number)
+ *                                or Title (String)
+ *  @param {String|Number} value  The value to be stored
+ */
+p5.TableRow.prototype.set = function(column, value) {
+  // if typeof column is string, use .obj
+  if (typeof(column) === 'string'){
+    var cPos = this.table.columns.indexOf(column); // index of columnID
+    if (cPos >= 0) {
+      this.obj[column] = value;
+      this.arr[cPos] = value;
+    }
+    else {
+      throw 'This table has no column named "' + column +'"';
+    }
+  }
+
+  // if typeof column is number, use .arr
+  else {
+    if (column < this.table.columns.length) {
+      this.arr[column] = value;
+      var cTitle = this.table.columns[column];
+      this.obj[cTitle] = value;
+    }
+    else {
+      throw 'Column #' + column + ' is out of the range of this table';
+    }
+  }
+};
+
+
+/**
+ *  Stores a Float value in the TableRow's specified column.
+ *  The column may be specified by either its ID or title.
+ *
+ *  @method  setNum
+ *  @param {String|Number} column Column ID (Number)
+ *                                or Title (String)
+ *  @param {Number} value  The value to be stored
+ *                                as a Float
+ */
+p5.TableRow.prototype.setNum = function(column, value){
+  var floatVal = parseFloat(value, 10);
+  this.set(column, floatVal);
+};
+
+
+/**
+ *  Stores a String value in the TableRow's specified column.
+ *  The column may be specified by either its ID or title.
+ *
+ *  @method  setString
+ *  @param {String|Number} column Column ID (Number)
+ *                                or Title (String)
+ *  @param {String} value  The value to be stored
+ *                                as a String
+ */
+p5.TableRow.prototype.setString = function(column, value){
+  var stringVal = value.toString();
+  this.set(column, stringVal);
+};
+
+/**
+ *  Retrieves a value from the TableRow's specified column.
+ *  The column may be specified by either its ID or title.
+ *
+ *  @method  get
+ *  @param  {String|Number} column columnName (string) or
+ *                                   ID (number)
+ *  @return {String|Number}
+ */
+p5.TableRow.prototype.get = function(column) {
+  if (typeof(column) === 'string'){
+    return this.obj[column];
+  } else {
+    return this.arr[column];
+  }
+};
+
+/**
+ *  Retrieves a Float value from the TableRow's specified
+ *  column. The column may be specified by either its ID or
+ *  title.
+ *
+ *  @method  getNum
+ *  @param  {String|Number} column columnName (string) or
+ *                                   ID (number)
+ *  @return {Number}  Float Floating point number
+ */
+p5.TableRow.prototype.getNum = function(column) {
+  var ret;
+  if (typeof(column) === 'string'){
+    ret = parseFloat(this.obj[column], 10);
+  } else {
+    ret = parseFloat(this.arr[column], 10);
+  }
+
+  if (ret.toString() === 'NaN') {
+    throw 'Error: ' + this.obj[column]+ ' is NaN (Not a Number)';
+  }
+  return ret;
+};
+
+/**
+ *  Retrieves an String value from the TableRow's specified
+ *  column. The column may be specified by either its ID or
+ *  title.
+ *
+ *  @method  getString
+ *  @param  {String|Number} column columnName (string) or
+ *                                   ID (number)
+ *  @return {String}  String
+ */
+p5.TableRow.prototype.getString = function(column) {
+  if (typeof(column) === 'string'){
+    return this.obj[column].toString();
+  } else {
+    return this.arr[column].toString();
+  }
+};
+
+module.exports = p5.TableRow;
+
+},{"../core/core":48}],73:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Calculation
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Calculates the absolute value (magnitude) of a number. Maps to Math.abs().
+ * The absolute value of a number is always positive.
+ *
+ * @method abs
+ * @param  {Number} n number to compute
+ * @return {Number}   absolute value of given number
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var x = -3;
+ *   var y = abs(x);
+ *
+ *   print(x); // -3
+ *   print(y); // 3
+ * }
+ * </code></div>
+ */
+p5.prototype.abs = Math.abs;
+
+/**
+ * Calculates the closest int value that is greater than or equal to the
+ * value of the parameter. Maps to Math.ceil(). For example, ceil(9.03)
+ * returns the value 10.
+ *
+ * @method ceil
+ * @param  {Number} n number to round up
+ * @return {Number}   rounded up number
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *   // map, mouseX between 0 and 5.
+ *   var ax = map(mouseX, 0, 100, 0, 5);
+ *   var ay = 66;
+ *
+ *   //Get the ceiling of the mapped number.
+ *   var bx = ceil(map(mouseX, 0, 100, 0,5));
+ *   var by = 33;
+ *
+ *   // Multiply the mapped numbers by 20 to more easily
+ *   // see the changes.
+ *   stroke(0);
+ *   fill(0);
+ *   line(0, ay, ax * 20, ay);
+ *   line(0, by, bx * 20, by);
+ *
+ *   // Reformat the float returned by map and draw it.
+ *   noStroke();
+ *   text(nfc(ax, 2,2), ax, ay - 5);
+ *   text(nfc(bx,1,1), bx, by - 5);
+ * }
+ * </code></div>
+ */
+p5.prototype.ceil = Math.ceil;
+
+/**
+ * Constrains a value between a minimum and maximum value.
+ *
+ * @method constrain
+ * @param  {Number} n    number to constrain
+ * @param  {Number} low  minimum limit
+ * @param  {Number} high maximum limit
+ * @return {Number}      constrained number
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *
+ *   var leftWall = 25;
+ *   var rightWall = 75;
+ *
+ *   // xm is just the mouseX, while
+ *   // xc is the mouseX, but constrained
+ *   // between the leftWall and rightWall!
+ *   var xm = mouseX;
+ *   var xc = constrain(mouseX, leftWall, rightWall);
+ *
+ *   // Draw the walls.
+ *   stroke(150);
+ *   line(leftWall, 0, leftWall, height);
+ *   line(rightWall, 0, rightWall, height);
+ *
+ *   // Draw xm and xc as circles.
+ *   noStroke();
+ *   fill(150);
+ *   ellipse(xm, 33, 9,9); // Not Constrained
+ *   fill(0);
+ *   ellipse(xc, 66, 9,9); // Constrained
+ * }
+ * </code></div>
+ */
+p5.prototype.constrain = function(n, low, high) {
+  return Math.max(Math.min(n, high), low);
+};
+
+/**
+ * Calculates the distance between two points.
+ *
+ * @method dist
+ * @param  {Number} x1 x-coordinate of the first point
+ * @param  {Number} y1 y-coordinate of the first point
+ * @param  {Number} [z1] z-coordinate of the first point
+ * @param  {Number} x2 x-coordinate of the second point
+ * @param  {Number} y2 y-coordinate of the second point
+ * @param  {Number} [z2] z-coordinate of the second point
+ * @return {Number}    distance between the two points
+ * @example
+ * <div><code>
+ * // Move your mouse inside the canvas to see the
+ * // change in distance between two points!
+ * function draw() {
+ *   background(200);
+ *   fill(0);
+ *
+ *   var x1 = 10;
+ *   var y1 = 90;
+ *   var x2 = mouseX;
+ *   var y2 = mouseY;
+ *
+ *   line(x1, y1, x2, y2);
+ *   ellipse(x1, y1, 7, 7);
+ *   ellipse(x2, y2, 7, 7);
+ *
+ *   // d is the length of the line
+ *   // the distance from point 1 to point 2.
+ *   var d = int(dist(x1, y1, x2, y2));
+ *
+ *   // Let's write d along the line we are drawing!
+ *   push();
+ *   translate( (x1+x2)/2, (y1+y2)/2 );
+ *   rotate( atan2(y2-y1,x2-x1) );
+ *   text(nfc(d,1,1), 0, -5);
+ *   pop();
+ *   // Fancy!
+ * }
+ * </code></div>
+ */
+p5.prototype.dist = function(x1, y1, z1, x2, y2, z2) {
+  if (arguments.length === 4) {
+    // In the case of 2d: z1 means x2 and x2 means y2
+    return Math.sqrt( (z1-x1)*(z1-x1) + (x2-y1)*(x2-y1) );
+  } else if (arguments.length === 6) {
+    return Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1) );
+  }
+};
+
+/**
+ * Returns Euler's number e (2.71828...) raised to the power of the n
+ * parameter. Maps to Math.exp().
+ *
+ * @method exp
+ * @param  {Number} n exponent to raise
+ * @return {Number}   e^n
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *
+ *   // Compute the exp() function with a value between 0 and 2
+ *   var xValue = map(mouseX, 0, width, 0, 2);
+ *   var yValue = exp(xValue);
+ *
+ *   var y = map(yValue, 0, 8, height, 0);
+ *
+ *   var legend = "exp (" + nfc(xValue, 3) +")\n= " + nf(yValue, 1, 4);
+ *   stroke(150);
+ *   line(mouseX, y, mouseX, height);
+ *   fill(0);
+ *   text(legend, 5, 15);
+ *   noStroke();
+ *   ellipse (mouseX,y, 7, 7);
+ *
+ *   // Draw the exp(x) curve,
+ *   // over the domain of x from 0 to 2
+ *   noFill();
+ *   stroke(0);
+ *   beginShape();
+ *   for (var x = 0; x < width; x++) {
+ *     xValue = map(x, 0, width, 0, 2);
+ *     yValue = exp(xValue);
+ *     y = map(yValue, 0, 8, height, 0);
+ *     vertex(x, y);
+ *   }
+ *
+ *   endShape();
+ *   line(0, 0, 0, height);
+ *   line(0, height-1, width, height-1);
+ * }
+ * </code></div>
+ */
+p5.prototype.exp = Math.exp;
+
+/**
+ * Calculates the closest int value that is less than or equal to the
+ * value of the parameter. Maps to Math.floor().
+ *
+ * @method floor
+ * @param  {Number} n number to round down
+ * @return {Number}   rounded down number
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *   //map, mouseX between 0 and 5.
+ *   var ax = map(mouseX, 0, 100, 0, 5);
+ *   var ay = 66;
+ *
+ *   //Get the floor of the mapped number.
+ *   var bx = floor(map(mouseX, 0, 100, 0,5));
+ *   var by = 33;
+ *
+ *   // Multiply the mapped numbers by 20 to more easily
+ *   // see the changes.
+ *   stroke(0);
+ *   fill(0);
+ *   line(0, ay, ax * 20, ay);
+ *   line(0, by, bx * 20, by);
+ *
+ *   // Reformat the float returned by map and draw it.
+ *   noStroke();
+ *   text(nfc(ax, 2,2), ax, ay - 5);
+ *   text(nfc(bx,1,1), bx, by - 5);
+ * }
+ * </code></div>
+ */
+p5.prototype.floor = Math.floor;
+
+/**
+ * Calculates a number between two numbers at a specific increment. The amt
+ * parameter is the amount to interpolate between the two values where 0.0
+ * equal to the first point, 0.1 is very near the first point, 0.5 is
+ * half-way in between, etc. The lerp function is convenient for creating
+ * motion along a straight path and for drawing dotted lines.
+ *
+ * @method lerp
+ * @param  {Number} start first value
+ * @param  {Number} stop  second value
+ * @param  {Number} amt   number between 0.0 and 1.0
+ * @return {Number}       lerped value
+ * @example
+ * <div><code>
+ * function setup() {
+ *   background(200);
+ *   var a = 20;
+ *   var b = 80;
+ *   var c = lerp(a,b, .2);
+ *   var d = lerp(a,b, .5);
+ *   var e = lerp(a,b, .8);
+ *
+ *   var y = 50
+ *
+ *   strokeWeight(5);
+ *   stroke(0); // Draw the original points in black
+ *   point(a, y);
+ *   point(b, y);
+ *
+ *   stroke(100); // Draw the lerp points in gray
+ *   point(c, y);
+ *   point(d, y);
+ *   point(e, y);
+ * }
+ * </code></div>
+ */
+p5.prototype.lerp = function(start, stop, amt) {
+  return amt*(stop-start)+start;
+};
+
+/**
+ * Calculates the natural logarithm (the base-e logarithm) of a number. This
+ * function expects the n parameter to be a value greater than 0.0. Maps to
+ * Math.log().
+ *
+ * @method log
+ * @param  {Number} n number greater than 0
+ * @return {Number}   natural logarithm of n
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *   var maxX = 2.8;
+ *   var maxY = 1.5;
+ *
+ *   // Compute the natural log of a value between 0 and maxX
+ *   var xValue = map(mouseX, 0, width, 0, maxX);
+ *   if (xValue > 0) { // Cannot take the log of a negative number.
+ *     var yValue = log(xValue);
+ *     var y = map(yValue, -maxY, maxY, height, 0);
+ *
+ *     // Display the calculation occurring.
+ *     var legend = "log(" + nf(xValue, 1, 2) + ")\n= " + nf(yValue, 1, 3);
+ *     stroke(150);
+ *     line(mouseX, y, mouseX, height);
+ *     fill(0);
+ *     text (legend, 5, 15);
+ *     noStroke();
+ *     ellipse (mouseX, y, 7, 7);
+ *   }
+ *
+ *   // Draw the log(x) curve,
+ *   // over the domain of x from 0 to maxX
+ *   noFill();
+ *   stroke(0);
+ *   beginShape();
+ *   for(var x=0; x < width; x++) {
+ *     xValue = map(x, 0, width, 0, maxX);
+ *     yValue = log(xValue);
+ *     y = map(yValue, -maxY, maxY, height, 0);
+ *     vertex(x, y);
+ *   }
+ *   endShape();
+ *   line(0,0,0,height);
+ *   line(0,height/2,width, height/2);
+ * }
+ * </code></div>
+ */
+p5.prototype.log = Math.log;
+
+/**
+ * Calculates the magnitude (or length) of a vector. A vector is a direction
+ * in space commonly used in computer graphics and linear algebra. Because it
+ * has no "start" position, the magnitude of a vector can be thought of as
+ * the distance from the coordinate 0,0 to its x,y value. Therefore, mag() is
+ * a shortcut for writing dist(0, 0, x, y).
+ *
+ * @method mag
+ * @param  {Number} a first value
+ * @param  {Number} b second value
+ * @return {Number}   magnitude of vector from (0,0) to (a,b)
+ * @example
+ * <div><code>
+ * function setup() {
+ *   var x1 = 20;
+ *   var x2 = 80;
+ *   var y1 = 30;
+ *   var y2 = 70;
+ *
+ *   line(0, 0, x1, y1);
+ *   print(mag(x1, y1));  // Prints "36.05551"
+ *   line(0, 0, x2, y1);
+ *   print(mag(x2, y1));  // Prints "85.44004"
+ *   line(0, 0, x1, y2);
+ *   print(mag(x1, y2));  // Prints "72.8011"
+ *   line(0, 0, x2, y2);
+ *   print(mag(x2, y2));  // Prints "106.30146"
+ * }
+ * </code></div>
+ */
+p5.prototype.mag = function(x, y) {
+  return Math.sqrt(x*x+y*y);
+};
+
+/**
+ * Re-maps a number from one range to another.
+ * <br><br>
+ * In the first example above, the number 25 is converted from a value in the
+ * range of 0 to 100 into a value that ranges from the left edge of the
+ * window (0) to the right edge (width).
+ *
+ * @method map
+ * @param  {Number} value  the incoming value to be converted
+ * @param  {Number} start1 lower bound of the value's current range
+ * @param  {Number} stop1  upper bound of the value's current range
+ * @param  {Number} start2 lower bound of the value's target range
+ * @param  {Number} stop   upper bound of the value's target range
+ * @return {Number}        remapped number
+ * @example
+ *   <div><code>
+ *     var value = 25;
+ *     var m = map(value, 0, 100, 0, width);
+ *     ellipse(m, 50, 10, 10);
+ *   </code></div>
+ *
+ *   <div><code>
+ *     function setup() {
+ *       noStroke();
+ *     }
+ *
+ *     function draw() {
+ *       background(204);
+ *       var x1 = map(mouseX, 0, width, 25, 75);
+ *       ellipse(x1, 25, 25, 25);
+ *       var x2 = map(mouseX, 0, width, 0, 100);
+ *       ellipse(x2, 75, 25, 25);
+ *     }
+ *   </code></div>
+ */
+p5.prototype.map = function(n, start1, stop1, start2, stop2) {
+  return ((n-start1)/(stop1-start1))*(stop2-start2)+start2;
+};
+
+/**
+ * Determines the largest value in a sequence of numbers, and then returns
+ * that value. max() accepts any number of Number parameters, or an Array
+ * of any length.
+ *
+ * @method max
+ * @param  {Number|Array} n0 Numbers to compare
+ * @return {Number}          maximum Number
+ * @example
+ * <div><code>
+ * function setup() {
+ *   // Change the elements in the array and run the sketch
+ *   // to show how max() works!
+ *   numArray = new Array(2,1,5,4,8,9);
+ *   fill(0);
+ *   noStroke();
+ *   text("Array Elements", 0, 10);
+ *   // Draw all numbers in the array
+ *   var spacing = 15;
+ *   var elemsY = 25;
+ *   for(var i = 0; i < numArray.length; i++) {
+ *     text(numArray[i], i * spacing, elemsY);
+ *   }
+ *   maxX = 33;
+ *   maxY = 80;
+ *   // Draw the Maximum value in the array.
+ *   textSize(32);
+ *   text(max(numArray), maxX, maxY);
+ * }
+ * </code></div>
+ */
+p5.prototype.max = function() {
+  if (arguments[0] instanceof Array) {
+    return Math.max.apply(null,arguments[0]);
+  } else {
+    return Math.max.apply(null,arguments);
+  }
+};
+
+/**
+ * Determines the smallest value in a sequence of numbers, and then returns
+ * that value. min() accepts any number of Number parameters, or an Array
+ * of any length.
+ *
+ * @method min
+ * @param  {Number|Array} n0 Numbers to compare
+ * @return {Number}          minimum Number
+ * @example
+ * <div><code>
+ * function setup() {
+ *   // Change the elements in the array and run the sketch
+ *   // to show how min() works!
+ *   numArray = new Array(2,1,5,4,8,9);
+ *   fill(0);
+ *   noStroke();
+ *   text("Array Elements", 0, 10);
+ *   // Draw all numbers in the array
+ *   var spacing = 15;
+ *   var elemsY = 25;
+ *   for(var i = 0; i < numArray.length; i++) {
+ *     text(numArray[i], i * spacing, elemsY);
+ *   }
+ *   maxX = 33;
+ *   maxY = 80;
+ *   // Draw the Minimum value in the array.
+ *   textSize(32);
+ *   text(min(numArray), maxX, maxY);
+ * }
+ * </code></div>
+ */
+p5.prototype.min = function() {
+  if (arguments[0] instanceof Array) {
+    return Math.min.apply(null,arguments[0]);
+  } else {
+    return Math.min.apply(null,arguments);
+  }
+};
+
+/**
+ * Normalizes a number from another range into a value between 0 and 1.
+ * Identical to map(value, low, high, 0, 1).
+ * Numbers outside of the range are not clamped to 0 and 1, because
+ * out-of-range values are often intentional and useful. (See the second
+ * example above.)
+ *
+ * @method norm
+ * @param  {Number} value incoming value to be normalized
+ * @param  {Number} start lower bound of the value's current range
+ * @param  {Number} stop  upper bound of the value's current range
+ * @return {Number}       normalized number
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *   currentNum = mouseX;
+ *   lowerBound = 0;
+ *   upperBound = width; //100;
+ *   normalized = norm(currentNum, lowerBound, upperBound);
+ *   lineY = 70
+ *   line(0, lineY, width, lineY);
+ *   //Draw an ellipse mapped to the non-normalized value.
+ *   noStroke();
+ *   fill(50)
+ *   var s = 7; // ellipse size
+ *   ellipse(currentNum, lineY, s, s);
+ *
+ *   // Draw the guide
+ *   guideY = lineY + 15;
+ *   text("0", 0, guideY);
+ *   textAlign(RIGHT);
+ *   text("100", width, guideY);
+ *
+ *   // Draw the normalized value
+ *   textAlign(LEFT);
+ *   fill(0);
+ *   textSize(32);
+ *   normalY = 40;
+ *   normalX = 20;
+ *   text(normalized, normalX, normalY);
+ * }
+ * </code></div>
+ */
+p5.prototype.norm = function(n, start, stop) {
+  return this.map(n, start, stop, 0, 1);
+};
+
+/**
+ * Facilitates exponential expressions. The pow() function is an efficient
+ * way of multiplying numbers by themselves (or their reciprocals) in large
+ * quantities. For example, pow(3, 5) is equivalent to the expression
+ * 3*3*3*3*3 and pow(3, -5) is equivalent to 1 / 3*3*3*3*3. Maps to
+ * Math.pow().
+ *
+ * @method pow
+ * @param  {Number} n base of the exponential expression
+ * @param  {Number} e power by which to raise the base
+ * @return {Number}   n^e
+ * @example
+ * <div><code>
+ * function setup() {
+ *   //Exponentially increase the size of an ellipse.
+ *   eSize = 3; // Original Size
+ *   eLoc = 10; // Original Location
+ *
+ *   ellipse(eLoc, eLoc, eSize, eSize);
+ *
+ *   ellipse(eLoc*2, eLoc*2, pow(eSize, 2), pow(eSize, 2));
+ *
+ *   ellipse(eLoc*4, eLoc*4, pow(eSize, 3), pow(eSize, 3));
+ *
+ *   ellipse(eLoc*8, eLoc*8, pow(eSize, 4), pow(eSize, 4));
+ * }
+ * </code></div>
+ */
+p5.prototype.pow = Math.pow;
+
+/**
+ * Calculates the integer closest to the n parameter. For example,
+ * round(133.8) returns the value 134. Maps to Math.round().
+ *
+ * @method round
+ * @param  {Number} n number to round
+ * @return {Number}   rounded number
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *   //map, mouseX between 0 and 5.
+ *   var ax = map(mouseX, 0, 100, 0, 5);
+ *   var ay = 66;
+ *
+ *   // Round the mapped number.
+ *   var bx = round(map(mouseX, 0, 100, 0,5));
+ *   var by = 33;
+ *
+ *   // Multiply the mapped numbers by 20 to more easily
+ *   // see the changes.
+ *   stroke(0);
+ *   fill(0);
+ *   line(0, ay, ax * 20, ay);
+ *   line(0, by, bx * 20, by);
+ *
+ *   // Reformat the float returned by map and draw it.
+ *   noStroke();
+ *   text(nfc(ax, 2,2), ax, ay - 5);
+ *   text(nfc(bx,1,1), bx, by - 5);
+ * }
+ * </code></div>
+ */
+p5.prototype.round = Math.round;
+
+/**
+ * Squares a number (multiplies a number by itself). The result is always a
+ * positive number, as multiplying two negative numbers always yields a
+ * positive result. For example, -1 * -1 = 1.
+ *
+ * @method sq
+ * @param  {Number} n number to square
+ * @return {Number}   squared number
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *   eSize = 7;
+ *   x1 = map(mouseX, 0, width, 0, 10);
+ *   y1 = 80;
+ *   x2 = sq(x1);
+ *   y2 = 20;
+ *
+ *   // Draw the non-squared.
+ *   line(0, y1, width, y1);
+ *   ellipse(x1, y1, eSize, eSize);
+ *
+ *   // Draw the squared.
+ *   line(0, y2, width, y2);
+ *   ellipse(x2, y2, eSize, eSize);
+ *
+ *   // Draw dividing line.
+ *   stroke(100)
+ *   line(0, height/2, width, height/2);
+ *
+ *   // Draw text.
+ *   var spacing = 15;
+ *   noStroke();
+ *   fill(0);
+ *   text("x = " + x1, 0, y1 + spacing);
+ *   text("sq(x) = " + x2, 0, y2 + spacing);
+ * }
+ * </code></div>
+ */
+p5.prototype.sq = function(n) { return n*n; };
+
+/**
+ * Calculates the square root of a number. The square root of a number is
+ * always positive, even though there may be a valid negative root. The
+ * square root s of number a is such that s*s = a. It is the opposite of
+ * squaring. Maps to Math.sqrt().
+ *
+ * @method sqrt
+ * @param  {Number} n non-negative number to square root
+ * @return {Number}   square root of number
+ * @example
+ * <div><code>
+ * function draw() {
+ *   background(200);
+ *   eSize = 7;
+ *   x1 = mouseX;
+ *   y1 = 80;
+ *   x2 = sqrt(x1);
+ *   y2 = 20;
+ *
+ *   // Draw the non-squared.
+ *   line(0, y1, width, y1);
+ *   ellipse(x1, y1, eSize, eSize);
+ *
+ *   // Draw the squared.
+ *   line(0, y2, width, y2);
+ *   ellipse(x2, y2, eSize, eSize);
+ *
+ *   // Draw dividing line.
+ *   stroke(100)
+ *   line(0, height/2, width, height/2);
+ *
+ *   // Draw text.
+ *   noStroke();
+ *   fill(0);
+ *   var spacing = 15;
+ *   text("x = " + x1, 0, y1 + spacing);
+ *   text("sqrt(x) = " + x2, 0, y2 + spacing);
+ * }
+ * </code></div>
+ */
+p5.prototype.sqrt = Math.sqrt;
+
+module.exports = p5;
+
+},{"../core/core":48}],74:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Math
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+
+/**
+ * Creates a new p5.Vector (the datatype for storing vectors). This provides a
+ * two or three dimensional vector, specifically a Euclidean (also known as
+ * geometric) vector. A vector is an entity that has both magnitude and
+ * direction.
+ *
+ * @method createVector
+ * @param {Number} [x] x component of the vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ */
+p5.prototype.createVector = function (x, y, z) {
+  if (this instanceof p5) {
+    return new p5.Vector(this, arguments);
+  } else {
+    return new p5.Vector(x, y, z);
+  }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],75:[function(_dereq_,module,exports){
+//////////////////////////////////////////////////////////////
+
+// http://mrl.nyu.edu/~perlin/noise/
+// Adapting from PApplet.java
+// which was adapted from toxi
+// which was adapted from the german demo group farbrausch
+// as used in their demo "art": http://www.farb-rausch.de/fr010src.zip
+
+// someday we might consider using "improved noise"
+// http://mrl.nyu.edu/~perlin/paper445.pdf
+// See: https://github.com/shiffman/The-Nature-of-Code-Examples-p5.js/
+//      blob/master/introduction/Noise1D/noise.js
+
+/**
+ * @module Math
+ * @submodule Noise
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+var PERLIN_YWRAPB = 4;
+var PERLIN_YWRAP = 1<<PERLIN_YWRAPB;
+var PERLIN_ZWRAPB = 8;
+var PERLIN_ZWRAP = 1<<PERLIN_ZWRAPB;
+var PERLIN_SIZE = 4095;
+
+var perlin_octaves = 4; // default to medium smooth
+var perlin_amp_falloff = 0.5; // 50% reduction/octave
+
+var scaled_cosine = function(i) {
+  return 0.5*(1.0-Math.cos(i*Math.PI));
+};
+
+var perlin; // will be initialized lazily by noise() or noiseSeed()
+
+
+/**
+ * Returns the Perlin noise value at specified coordinates. Perlin noise is
+ * a random sequence generator producing a more natural ordered, harmonic
+ * succession of numbers compared to the standard <b>random()</b> function.
+ * It was invented by Ken Perlin in the 1980s and been used since in
+ * graphical applications to produce procedural textures, natural motion,
+ * shapes, terrains etc.<br /><br /> The main difference to the
+ * <b>random()</b> function is that Perlin noise is defined in an infinite
+ * n-dimensional space where each pair of coordinates corresponds to a
+ * fixed semi-random value (fixed only for the lifespan of the program; see
+ * the noiseSeed() function). p5.js can compute 1D, 2D and 3D noise,
+ * depending on the number of coordinates given. The resulting value will
+ * always be between 0.0 and 1.0. The noise value can be animated by moving
+ * through the noise space as demonstrated in the example above. The 2nd
+ * and 3rd dimension can also be interpreted as time.<br /><br />The actual
+ * noise is structured similar to an audio signal, in respect to the
+ * function's use of frequencies. Similar to the concept of harmonics in
+ * physics, perlin noise is computed over several octaves which are added
+ * together for the final result. <br /><br />Another way to adjust the
+ * character of the resulting sequence is the scale of the input
+ * coordinates. As the function works within an infinite space the value of
+ * the coordinates doesn't matter as such, only the distance between
+ * successive coordinates does (eg. when using <b>noise()</b> within a
+ * loop). As a general rule the smaller the difference between coordinates,
+ * the smoother the resulting noise sequence will be. Steps of 0.005-0.03
+ * work best for most applications, but this will differ depending on use.
+ *
+ *
+ * @method noise
+ * @param  {Number} x   x-coordinate in noise space
+ * @param  {Number} y   y-coordinate in noise space
+ * @param  {Number} z   z-coordinate in noise space
+ * @return {Number}     Perlin noise value (between 0 and 1) at specified
+ *                      coordinates
+ * @example
+ * <div>
+ * <code>var xoff = 0.0;
+ *
+ * function draw() {
+ *   background(204);
+ *   xoff = xoff + .01;
+ *   var n = noise(xoff) * width;
+ *   line(n, 0, n, height);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>var noiseScale=0.02;
+ *
+ * function draw() {
+ *   background(0);
+ *   for (var x=0; x < width; x++) {
+ *     var noiseVal = noise((mouseX+x)*noiseScale, mouseY*noiseScale);
+ *     stroke(noiseVal*255);
+ *     line(x, mouseY+noiseVal*80, x, height);
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noise = function(x,y,z) {
+  y = y || 0;
+  z = z || 0;
+
+  if (perlin == null) {
+    perlin = new Array(PERLIN_SIZE + 1);
+    for (var i = 0; i < PERLIN_SIZE + 1; i++) {
+      perlin[i] = Math.random();
+    }
+  }
+
+  if (x<0) { x=-x; }
+  if (y<0) { y=-y; }
+  if (z<0) { z=-z; }
+
+  var xi=Math.floor(x), yi=Math.floor(y), zi=Math.floor(z);
+  var xf = x - xi;
+  var yf = y - yi;
+  var zf = z - zi;
+  var rxf, ryf;
+
+  var r=0;
+  var ampl=0.5;
+
+  var n1,n2,n3;
+
+  for (var o=0; o<perlin_octaves; o++) {
+    var of=xi+(yi<<PERLIN_YWRAPB)+(zi<<PERLIN_ZWRAPB);
+
+    rxf = scaled_cosine(xf);
+    ryf = scaled_cosine(yf);
+
+    n1  = perlin[of&PERLIN_SIZE];
+    n1 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n1);
+    n2  = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
+    n2 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n2);
+    n1 += ryf*(n2-n1);
+
+    of += PERLIN_ZWRAP;
+    n2  = perlin[of&PERLIN_SIZE];
+    n2 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n2);
+    n3  = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
+    n3 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n3);
+    n2 += ryf*(n3-n2);
+
+    n1 += scaled_cosine(zf)*(n2-n1);
+
+    r += n1*ampl;
+    ampl *= perlin_amp_falloff;
+    xi<<=1;
+    xf*=2;
+    yi<<=1;
+    yf*=2;
+    zi<<=1;
+    zf*=2;
+
+    if (xf>=1.0) { xi++; xf--; }
+    if (yf>=1.0) { yi++; yf--; }
+    if (zf>=1.0) { zi++; zf--; }
+  }
+  return r;
+};
+
+
+/**
+ *
+ * Adjusts the character and level of detail produced by the Perlin noise
+ * function. Similar to harmonics in physics, noise is computed over
+ * several octaves. Lower octaves contribute more to the output signal and
+ * as such define the overall intensity of the noise, whereas higher octaves
+ * create finer grained details in the noise sequence.
+ * <br><br>
+ * By default, noise is computed over 4 octaves with each octave contributing
+ * exactly half than its predecessor, starting at 50% strength for the 1st
+ * octave. This falloff amount can be changed by adding an additional function
+ * parameter. Eg. a falloff factor of 0.75 means each octave will now have
+ * 75% impact (25% less) of the previous lower octave. Any value between
+ * 0.0 and 1.0 is valid, however note that values greater than 0.5 might
+ * result in greater than 1.0 values returned by <b>noise()</b>.
+ * <br><br>
+ * By changing these parameters, the signal created by the <b>noise()</b>
+ * function can be adapted to fit very specific needs and characteristics.
+ *
+ * @method noiseDetail
+ * @param {Number} lod number of octaves to be used by the noise
+ * @param {Number} falloff falloff factor for each octave
+ * @example
+ * <div>
+ * <code>
+ *
+ * var noiseVal;
+ * var noiseScale=0.02;
+ *
+ * function setup() {
+ *   createCanvas(100,100);
+ * }
+ *
+ * function draw() {
+ *   background(0);
+ *   for (var y = 0; y < height; y++) {
+ *     for (var x = 0; x < width/2; x++) {
+ *       noiseDetail(2,0.2);
+ *       noiseVal = noise((mouseX+x) * noiseScale,
+ *                        (mouseY+y) * noiseScale);
+ *       stroke(noiseVal*255);
+ *       point(x,y);
+ *       noiseDetail(8,0.65);
+ *       noiseVal = noise((mouseX + x + width/2) * noiseScale,
+ *                        (mouseY + y) * noiseScale);
+ *       stroke(noiseVal*255);
+ *       point(x + width/2, y);
+ *     }
+ *   }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noiseDetail = function(lod, falloff) {
+  if (lod>0)     { perlin_octaves=lod; }
+  if (falloff>0) { perlin_amp_falloff=falloff; }
+};
+
+/**
+ * Sets the seed value for <b>noise()</b>. By default, <b>noise()</b>
+ * produces different results each time the program is run. Set the
+ * <b>value</b> parameter to a constant to return the same pseudo-random
+ * numbers each time the software is run.
+ *
+ * @method noiseSeed
+ * @param {Number} seed   the seed value
+ * @example
+ * <div>
+ * <code>var xoff = 0.0;
+ *
+ * function setup() {
+ *   noiseSeed(99);
+ *   stroke(0, 10);
+ * }
+ *
+ * function draw() {
+ *   xoff = xoff + .01;
+ *   var n = noise(xoff) * width;
+ *   line(n, 0, n, height);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noiseSeed = function(seed) {
+  // Linear Congruential Generator
+  // Variant of a Lehman Generator
+  var lcg = (function() {
+    // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
+    // m is basically chosen to be large (as it is the max period)
+    // and for its relationships to a and c
+    var m = 4294967296,
+    // a - 1 should be divisible by m's prime factors
+    a = 1664525,
+     // c and m should be co-prime
+    c = 1013904223,
+    seed, z;
+    return {
+      setSeed : function(val) {
+        // pick a random seed if val is undefined or null
+        // the >>> 0 casts the seed to an unsigned 32-bit integer
+        z = seed = (val == null ? Math.random() * m : val) >>> 0;
+      },
+      getSeed : function() {
+        return seed;
+      },
+      rand : function() {
+        // define the recurrence relationship
+        z = (a * z + c) % m;
+        // return a float in [0, 1)
+        // if z = m then z / m = 0 therefore (z % m) / m < 1 always
+        return z / m;
+      }
+    };
+  }());
+
+  lcg.setSeed(seed);
+  perlin = new Array(PERLIN_SIZE + 1);
+  for (var i = 0; i < PERLIN_SIZE + 1; i++) {
+    perlin[i] = lcg.rand();
+  }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],76:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Math
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('./polargeometry');
+var constants = _dereq_('../core/constants');
+
+/**
+ * A class to describe a two or three dimensional vector, specifically
+ * a Euclidean (also known as geometric) vector. A vector is an entity
+ * that has both magnitude and direction. The datatype, however, stores
+ * the components of the vector (x, y for 2D, and x, y, z for 3D). The magnitude
+ * and direction can be accessed via the methods mag() and heading().
+ * <br><br>
+ * In many of the p5.js examples, you will see p5.Vector used to describe a
+ * position, velocity, or acceleration. For example, if you consider a rectangle
+ * moving across the screen, at any given instant it has a position (a vector
+ * that points from the origin to its location), a velocity (the rate at which
+ * the object's position changes per time unit, expressed as a vector), and
+ * acceleration (the rate at which the object's velocity changes per time
+ * unit, expressed as a vector).
+ * <br><br>
+ * Since vectors represent groupings of values, we cannot simply use
+ * traditional addition/multiplication/etc. Instead, we'll need to do some
+ * "vector" math, which is made easy by the methods inside the p5.Vector class.
+ *
+ * @class p5.Vector
+ * @constructor
+ * @param {Number} [x] x component of the vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ * @example
+ * <div>
+ * <code>
+ * var v1 = createVector(40, 50);
+ * var v2 = createVector(40, 50);
+ *
+ * ellipse(v1.x, v1.y, 50, 50);
+ * ellipse(v2.x, v2.y, 50, 50);
+ * v1.add(v2);
+ * ellipse(v1.x, v1.y, 50, 50);
+ * </code>
+ * </div>
+ */
+p5.Vector = function() {
+  var x,y,z;
+  // This is how it comes in with createVector()
+  if(arguments[0] instanceof p5) {
+    // save reference to p5 if passed in
+    this.p5 = arguments[0];
+    x  = arguments[1][0] || 0;
+    y  = arguments[1][1] || 0;
+    z  = arguments[1][2] || 0;
+  // This is what we'll get with new p5.Vector()
+  } else {
+    x = arguments[0] || 0;
+    y = arguments[1] || 0;
+    z = arguments[2] || 0;
+  }
+  /**
+   * The x component of the vector
+   * @property x
+   * @type {Number}
+   */
+  this.x = x;
+  /**
+   * The y component of the vector
+   * @property y
+   * @type {Number}
+   */
+  this.y = y;
+  /**
+   * The z component of the vector
+   * @property z
+   * @type {Number}
+   */
+  this.z = z;
+};
+
+/**
+ * Returns a string representation of a vector v by calling String(v)
+ * or v.toString(). This method is useful for logging vectors in the
+ * console.
+ * @method  toString
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var v = createVector(20,30);
+ *   print(String(v)); // prints "p5.Vector Object : [20, 30, 0]"
+ * }
+ * </div></code>
+ *
+ */
+p5.Vector.prototype.toString = function p5VectorToString() {
+  return 'p5.Vector Object : ['+ this.x +', '+ this.y +', '+ this.z + ']';
+};
+
+/**
+ * Sets the x, y, and z component of the vector using two or three separate
+ * variables, the data from a p5.Vector, or the values from a float array.
+ * @method set
+ *
+ * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
+ *                                     p5.Vector or an Array
+ * @param {Number}                 [y] the y component of the vector
+ * @param {Number}                 [z] the z component of the vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * function setup() {
+ *    var v = createVector(1, 2, 3);
+ *    v.set(4,5,6); // Sets vector to [4, 5, 6]
+ *
+ *    var v1 = createVector(0, 0, 0);
+ *    var arr = [1, 2, 3];
+ *    v1.set(arr); // Sets vector to [1, 2, 3]
+ * }
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.set = function (x, y, z) {
+  if (x instanceof p5.Vector) {
+    this.x = x.x || 0;
+    this.y = x.y || 0;
+    this.z = x.z || 0;
+    return this;
+  }
+  if (x instanceof Array) {
+    this.x = x[0] || 0;
+    this.y = x[1] || 0;
+    this.z = x[2] || 0;
+    return this;
+  }
+  this.x = x || 0;
+  this.y = y || 0;
+  this.z = z || 0;
+  return this;
+};
+
+/**
+ * Gets a copy of the vector, returns a p5.Vector object.
+ *
+ * @method copy
+ * @return {p5.Vector} the copy of the p5.Vector object
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = v.copy();
+ * print(v1.x == v2.x && v1.y == v2.y && v1.z == v2.z);
+ * // Prints "true"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.copy = function () {
+  if (this.p5) {
+    return new p5.Vector(this.p5,[this.x, this.y, this.z]);
+  } else {
+    return new p5.Vector(this.x,this.y,this.z);
+  }
+};
+
+/**
+ * Adds x, y, and z components to a vector, adds one vector to another, or
+ * adds two independent vectors together. The version of the method that adds
+ * two vectors together is a static method and returns a p5.Vector, the others
+ * acts directly on the vector. See the examples for more context.
+ *
+ * @method add
+ * @chainable
+ * @param  {Number|p5.Vector|Array} x   the x component of the vector to be
+ *                                      added or a p5.Vector or an Array
+ * @param  {Number}                 [y] the y component of the vector to be
+ *                                      added
+ * @param  {Number}                 [z] the z component of the vector to be
+ *                                      added
+ * @return {p5.Vector}                  the p5.Vector object.
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(1, 2, 3);
+ * v.add(4,5,6);
+ * // v's compnents are set to [5, 7, 9]
+ * </code>
+ * </div>
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(2, 3, 4);
+ *
+ * var v3 = p5.Vector.add(v1, v2);
+ * // v3 has components [3, 5, 7]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.add = function (x, y, z) {
+  if (x instanceof p5.Vector) {
+    this.x += x.x || 0;
+    this.y += x.y || 0;
+    this.z += x.z || 0;
+    return this;
+  }
+  if (x instanceof Array) {
+    this.x += x[0] || 0;
+    this.y += x[1] || 0;
+    this.z += x[2] || 0;
+    return this;
+  }
+  this.x += x || 0;
+  this.y += y || 0;
+  this.z += z || 0;
+  return this;
+};
+
+/**
+ * Subtracts x, y, and z components from a vector, subtracts one vector from
+ * another, or subtracts two independent vectors. The version of the method
+ * that subtracts two vectors is a static method and returns a p5.Vector, the
+ * other acts directly on the vector. See the examples for more context.
+ *
+ * @method sub
+ * @chainable
+ * @param  {Number|p5.Vector|Array} x   the x component of the vector or a
+ *                                      p5.Vector or an Array
+ * @param  {Number}                 [y] the y component of the vector
+ * @param  {Number}                 [z] the z component of the vector
+ * @return {p5.Vector}                  p5.Vector object.
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(4, 5, 6);
+ * v.sub(1, 1, 1);
+ * // v's compnents are set to [3, 4, 5]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(2, 3, 4);
+ * var v2 = createVector(1, 2, 3);
+ *
+ * var v3 = p5.Vector.sub(v1, v2);
+ * // v3 has compnents [1, 1, 1]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.sub = function (x, y, z) {
+  if (x instanceof p5.Vector) {
+    this.x -= x.x || 0;
+    this.y -= x.y || 0;
+    this.z -= x.z || 0;
+    return this;
+  }
+  if (x instanceof Array) {
+    this.x -= x[0] || 0;
+    this.y -= x[1] || 0;
+    this.z -= x[2] || 0;
+    return this;
+  }
+  this.x -= x || 0;
+  this.y -= y || 0;
+  this.z -= z || 0;
+  return this;
+};
+
+/**
+ * Multiply the vector by a scalar. The static version of this method
+ * creates a new p5.Vector while the non static version acts on the vector
+ * directly. See the examples for more context.
+ *
+ * @method mult
+ * @chainable
+ * @param  {Number}    n the number to multiply with the vector
+ * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(1, 2, 3);
+ * v.mult(2);
+ * // v's compnents are set to [2, 4, 6]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = p5.Vector.mult(v1, 2);
+ * // v2 has compnents [2, 4, 6]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.mult = function (n) {
+  this.x *= n || 0;
+  this.y *= n || 0;
+  this.z *= n || 0;
+  return this;
+};
+
+/**
+ * Divide the vector by a scalar. The static version of this method creates a
+ * new p5.Vector while the non static version acts on the vector directly.
+ * See the examples for more context.
+ *
+ * @method div
+ * @chainable
+ * @param  {number}    n the number to divide the vector by
+ * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(6, 4, 2);
+ * v.div(2); //v's compnents are set to [3, 2, 1]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1  = createVector(6, 4, 2);
+ * var v2 = p5.Vector.div(v, 2);
+ * // v2 has compnents [3, 2, 1]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.div = function (n) {
+  this.x /= n;
+  this.y /= n;
+  this.z /= n;
+  return this;
+};
+
+/**
+ * Calculates the magnitude (length) of the vector and returns the result as
+ * a float (this is simply the equation sqrt(x*x + y*y + z*z).)
+ *
+ * @method mag
+ * @return {Number} magnitude of the vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(20.0, 30.0, 40.0);
+ * var m = v.mag(10);
+ * print(m); // Prints "53.85164807134504"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.mag = function () {
+  return Math.sqrt(this.magSq());
+};
+
+/**
+ * Calculates the squared magnitude of the vector and returns the result
+ * as a float (this is simply the equation <em>(x*x + y*y + z*z)</em>.)
+ * Faster if the real length is not required in the
+ * case of comparing vectors, etc.
+ *
+ * @method magSq
+ * @return {number} squared magnitude of the vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(6, 4, 2);
+ * print(v1.magSq()); // Prints "56"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.magSq = function () {
+  var x = this.x, y = this.y, z = this.z;
+  return (x * x + y * y + z * z);
+};
+
+/**
+ * Calculates the dot product of two vectors. The version of the method
+ * that computes the dot product of two independent vectors is a static
+ * method. See the examples for more context.
+ *
+ *
+ * @method dot
+ * @param  {Number|p5.Vector} x   x component of the vector or a p5.Vector
+ * @param  {Number}           [y] y component of the vector
+ * @param  {Number}           [z] z component of the vector
+ * @return {Number}                 the dot product
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(2, 3, 4);
+ *
+ * print(v1.dot(v2)); // Prints "20"
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * //Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(3, 2, 1);
+ * print (p5.Vector.dot(v1, v2)); // Prints "10"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.dot = function (x, y, z) {
+  if (x instanceof p5.Vector) {
+    return this.dot(x.x, x.y, x.z);
+  }
+  return this.x * (x || 0) +
+         this.y * (y || 0) +
+         this.z * (z || 0);
+};
+
+/**
+ * Calculates and returns a vector composed of the cross product between
+ * two vectors. Both the static and non static methods return a new p5.Vector.
+ * See the examples for more context.
+ *
+ * @method cross
+ * @param  {p5.Vector} v p5.Vector to be crossed
+ * @return {p5.Vector}   p5.Vector composed of cross product
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(1, 2, 3);
+ *
+ * v1.cross(v2); // v's components are [0, 0, 0]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var crossProduct = p5.Vector.cross(v1, v2);
+ * // crossProduct has components [0, 0, 1]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.cross = function (v) {
+  var x = this.y * v.z - this.z * v.y;
+  var y = this.z * v.x - this.x * v.z;
+  var z = this.x * v.y - this.y * v.x;
+  if (this.p5) {
+    return new p5.Vector(this.p5,[x,y,z]);
+  } else {
+    return new p5.Vector(x,y,z);
+  }
+};
+
+/**
+ * Calculates the Euclidean distance between two points (considering a
+ * point as a vector object).
+ *
+ * @method dist
+ * @param  {p5.Vector} v the x, y, and z coordinates of a p5.Vector
+ * @return {Number}      the distance
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var distance = v1.dist(v2); // distance is 1.4142...
+ * </code>
+ * </div>
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var distance = p5.Vector.dist(v1,v2);
+ * // distance is 1.4142...
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.dist = function (v) {
+  var d = v.copy().sub(this);
+  return d.mag();
+};
+
+/**
+ * Normalize the vector to length 1 (make it a unit vector).
+ *
+ * @method normalize
+ * @return {p5.Vector} normalized p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v.normalize();
+ * // v's compnents are set to
+ * // [0.4454354, 0.8908708, 0.089087084]
+ * </code>
+ * </div>
+ *
+ */
+p5.Vector.prototype.normalize = function () {
+  return this.div(this.mag());
+};
+
+/**
+ * Limit the magnitude of this vector to the value used for the <b>max</b>
+ * parameter.
+ *
+ * @method limit
+ * @param  {Number}    max the maximum magnitude for the vector
+ * @return {p5.Vector}     the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v.limit(5);
+ * // v's compnents are set to
+ * // [2.2271771, 4.4543543, 0.4454354]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.limit = function (l) {
+  var mSq = this.magSq();
+  if(mSq > l*l) {
+    this.div(Math.sqrt(mSq)); //normalize it
+    this.mult(l);
+  }
+  return this;
+};
+
+/**
+ * Set the magnitude of this vector to the value used for the <b>len</b>
+ * parameter.
+ *
+ * @method setMag
+ * @param  {number}    len the new length for this vector
+ * @return {p5.Vector}     the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v1.setMag(10);
+ * // v's compnents are set to [6.0, 8.0, 0.0]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.setMag = function (n) {
+  return this.normalize().mult(n);
+};
+
+/**
+ * Calculate the angle of rotation for this vector (only 2D vectors)
+ *
+ * @method heading
+ * @return {Number} the angle of rotation
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var v1 = createVector(30,50);
+ *   print(v1.heading()); // 1.0303768265243125
+ *
+ *   var v1 = createVector(40,50);
+ *   print(v1.heading()); // 0.8960553845713439
+ *
+ *   var v1 = createVector(30,70);
+ *   print(v1.heading()); // 1.1659045405098132
+ * }
+ * </div></code>
+ */
+p5.Vector.prototype.heading = function () {
+  var h = Math.atan2(this.y, this.x);
+  if (this.p5) {
+    if (this.p5._angleMode === constants.RADIANS) {
+      return h;
+    } else {
+      return polarGeometry.radiansToDegrees(h);
+    }
+  } else {
+    return h;
+  }
+};
+
+/**
+ * Rotate the vector by an angle (only 2D vectors), magnitude remains the
+ * same
+ *
+ * @method rotate
+ * @param  {number}    angle the angle of rotation
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10.0, 20.0);
+ * // v has compnents [10.0, 20.0, 0.0]
+ * v.rotate(HALF_PI);
+ * // v's compnents are set to [-20.0, 9.999999, 0.0]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.rotate = function (a) {
+  if (this.p5) {
+    if (this.p5._angleMode === constants.DEGREES) {
+      a = polarGeometry.degreesToRadians(a);
+    }
+  }
+  var newHeading = this.heading() + a;
+  var mag = this.mag();
+  this.x = Math.cos(newHeading) * mag;
+  this.y = Math.sin(newHeading) * mag;
+  return this;
+};
+
+/**
+ * Linear interpolate the vector to another vector
+ *
+ * @method lerp
+ * @param  {p5.Vector} x   the x component or the p5.Vector to lerp to
+ * @param  {p5.Vector} [y] y the y component
+ * @param  {p5.Vector} [z] z the z component
+ * @param  {Number}    amt the amount of interpolation; some value between 0.0
+ *                         (old vector) and 1.0 (new vector). 0.1 is very near
+ *                         the new vector. 0.5 is halfway in between.
+ * @return {p5.Vector}     the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(1, 1, 0);
+ *
+ * v.lerp(3, 3, 0, 0.5); // v now has components [2,2,0]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(0, 0, 0);
+ * var v2 = createVector(100, 100, 0);
+ *
+ * var v3 = p5.Vector.lerp(v1, v2, 0.5);
+ * // v3 has components [50,50,0]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.lerp = function (x, y, z, amt) {
+  if (x instanceof p5.Vector) {
+    return this.lerp(x.x, x.y, x.z, y);
+  }
+  this.x += (x - this.x) * amt || 0;
+  this.y += (y - this.y) * amt || 0;
+  this.z += (z - this.z) * amt || 0;
+  return this;
+};
+
+/**
+ * Return a representation of this vector as a float array. This is only
+ * for temporary use. If used in any other fashion, the contents should be
+ * copied by using the <b>p5.Vector.copy()</b> method to copy into your own
+ * array.
+ *
+ * @method array
+ * @return {Array} an Array with the 3 values
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var v = createVector(20,30);
+ *   print(v.array()); // Prints : Array [20, 30, 0]
+ * }
+ * </div></code>
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10.0, 20.0, 30.0);
+ * var f = v.array();
+ * print(f[0]); // Prints "10.0"
+ * print(f[1]); // Prints "20.0"
+ * print(f[2]); // Prints "30.0"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.array = function () {
+  return [this.x || 0, this.y || 0, this.z || 0];
+};
+
+/**
+ * Equality check against a p5.Vector
+ *
+ * @method equals
+ * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
+ *                                     p5.Vector or an Array
+ * @param {Number}                 [y] the y component of the vector
+ * @param {Number}                 [z] the z component of the vector
+ * @return {Boolean} whether the vectors are equals
+ * @example
+ * <div class = "norender"><code>
+ * v1 = createVector(5,10,20);
+ * v2 = createVector(5,10,20);
+ * v3 = createVector(13,10,19);
+ *
+ * print(v1.equals(v2.x,v2.y,v2.z)); // true
+ * print(v1.equals(v3.x,v3.y,v3.z)); // false
+ * </div></code>
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(10.0, 20.0, 30.0);
+ * var v2 = createVector(10.0, 20.0, 30.0);
+ * var v3 = createVector(0.0, 0.0, 0.0);
+ * print (v1.equals(v2)) // true
+ * print (v1.equals(v3)) // false
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.equals = function (x, y, z) {
+  var a, b, c;
+  if (x instanceof p5.Vector) {
+    a = x.x || 0;
+    b = x.y || 0;
+    c = x.z || 0;
+  } else if (x instanceof Array) {
+    a = x[0] || 0;
+    b = x[1] || 0;
+    c = x[2] || 0;
+  } else {
+    a = x || 0;
+    b = y || 0;
+    c = z || 0;
+  }
+  return this.x === a && this.y === b && this.z === c;
+};
+
+
+// Static Methods
+
+
+/**
+ * Make a new 2D unit vector from an angle
+ *
+ * @method fromAngle
+ * @static
+ * @param {Number}     angle the desired angle
+ * @return {p5.Vector}       the new p5.Vector object
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ *   background (200);
+ *
+ *   // Create a variable, proportional to the mouseX,
+ *   // varying from 0-360, to represent an angle in degrees.
+ *   angleMode(DEGREES);
+ *   var myDegrees = map(mouseX, 0,width, 0,360);
+ *
+ *   // Display that variable in an onscreen text.
+ *   // (Note the nfc() function to truncate additional decimal places,
+ *   // and the "\xB0" character for the degree symbol.)
+ *   var readout = "angle = " + nfc(myDegrees,1,1) + "\xB0"
+ *   noStroke();
+ *   fill (0);
+ *   text (readout, 5, 15);
+ *
+ *   // Create a p5.Vector using the fromAngle function,
+ *   // and extract its x and y components.
+ *   var v = p5.Vector.fromAngle(radians(myDegrees));
+ *   var vx = v.x;
+ *   var vy = v.y;
+ *
+ *   push();
+ *   translate (width/2, height/2);
+ *   noFill();
+ *   stroke (150);
+ *   line (0,0, 30,0);
+ *   stroke (0);
+ *   line (0,0, 30*vx, 30*vy);
+ *   pop()
+ * }
+ * </code>
+ * </div>
+ */
+p5.Vector.fromAngle = function(angle) {
+  if (this.p5) {
+    if (this.p5._angleMode === constants.DEGREES) {
+      angle = polarGeometry.degreesToRadians(angle);
+    }
+  }
+  if (this.p5) {
+    return new p5.Vector(this.p5,[Math.cos(angle),Math.sin(angle),0]);
+  } else {
+    return new p5.Vector(Math.cos(angle),Math.sin(angle),0);
+  }
+};
+
+/**
+ * Make a new 2D unit vector from a random angle
+ *
+ * @method random2D
+ * @static
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = p5.Vector.random2D();
+ * // May make v's attributes something like:
+ * // [0.61554617, -0.51195765, 0.0] or
+ * // [-0.4695841, -0.14366731, 0.0] or
+ * // [0.6091097, -0.22805278, 0.0]
+ * </code>
+ * </div>
+ */
+p5.Vector.random2D = function () {
+  var angle;
+  // A lot of nonsense to determine if we know about a
+  // p5 sketch and whether we should make a random angle in degrees or radians
+  if (this.p5) {
+    if (this.p5._angleMode === constants.DEGREES) {
+      angle = this.p5.random(360);
+    } else {
+      angle = this.p5.random(constants.TWO_PI);
+    }
+  } else {
+    angle = Math.random()*Math.PI*2;
+  }
+  return this.fromAngle(angle);
+};
+
+/**
+ * Make a new random 3D unit vector.
+ *
+ * @method random3D
+ * @static
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = p5.Vector.random3D();
+ * // May make v's attributes something like:
+ * // [0.61554617, -0.51195765, 0.599168] or
+ * // [-0.4695841, -0.14366731, -0.8711202] or
+ * // [0.6091097, -0.22805278, -0.7595902]
+ * </code>
+ * </div>
+ */
+p5.Vector.random3D = function () {
+  var angle,vz;
+  // If we know about p5
+  if (this.p5) {
+    angle = this.p5.random(0,constants.TWO_PI);
+    vz = this.p5.random(-1,1);
+  } else {
+    angle = Math.random()*Math.PI*2;
+    vz = Math.random()*2-1;
+  }
+  var vx = Math.sqrt(1-vz*vz)*Math.cos(angle);
+  var vy = Math.sqrt(1-vz*vz)*Math.sin(angle);
+  if (this.p5) {
+    return new p5.Vector(this.p5,[vx,vy,vz]);
+  } else {
+    return new p5.Vector(vx,vy,vz);
+  }
+};
+
+
+/**
+ * Adds two vectors together and returns a new one.
+ *
+ * @static
+ * @param  {p5.Vector} v1 a p5.Vector to add
+ * @param  {p5.Vector} v2 a p5.Vector to add
+ * @param  {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting p5.Vector
+ *
+ */
+
+p5.Vector.add = function (v1, v2, target) {
+  if (!target) {
+    target = v1.copy();
+  } else {
+    target.set(v1);
+  }
+  target.add(v2);
+  return target;
+};
+
+/**
+ * Subtracts one p5.Vector from another and returns a new one.  The second
+ * vector (v2) is subtracted from the first (v1), resulting in v1-v2.
+ *
+ * @static
+ * @param  {p5.Vector} v1 a p5.Vector to subtract from
+ * @param  {p5.Vector} v2 a p5.Vector to subtract
+ * @param  {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting p5.Vector
+ */
+
+p5.Vector.sub = function (v1, v2, target) {
+  if (!target) {
+    target = v1.copy();
+  } else {
+    target.set(v1);
+  }
+  target.sub(v2);
+  return target;
+};
+
+
+/**
+ * Multiplies a vector by a scalar and returns a new vector.
+ *
+ * @static
+ * @param  {p5.Vector} v the p5.Vector to multiply
+ * @param  {Number}  n the scalar
+ * @param  {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector}  the resulting new p5.Vector
+ */
+p5.Vector.mult = function (v, n, target) {
+  if (!target) {
+    target = v.copy();
+  } else {
+    target.set(v);
+  }
+  target.mult(n);
+  return target;
+};
+
+/**
+ * Divides a vector by a scalar and returns a new vector.
+ *
+ * @static
+ * @param  {p5.Vector} v the p5.Vector to divide
+ * @param  {Number}  n the scalar
+ * @param  {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting new p5.Vector
+ */
+p5.Vector.div = function (v, n, target) {
+  if (!target) {
+    target = v.copy();
+  } else {
+    target.set(v);
+  }
+  target.div(n);
+  return target;
+};
+
+
+/**
+ * Calculates the dot product of two vectors.
+ *
+ * @static
+ * @param  {p5.Vector} v1 the first p5.Vector
+ * @param  {p5.Vector} v2 the second p5.Vector
+ * @return {Number}     the dot product
+ */
+p5.Vector.dot = function (v1, v2) {
+  return v1.dot(v2);
+};
+
+/**
+ * Calculates the cross product of two vectors.
+ *
+ * @static
+ * @param  {p5.Vector} v1 the first p5.Vector
+ * @param  {p5.Vector} v2 the second p5.Vector
+ * @return {Number}     the cross product
+ */
+p5.Vector.cross = function (v1, v2) {
+  return v1.cross(v2);
+};
+
+/**
+ * Calculates the Euclidean distance between two points (considering a
+ * point as a vector object).
+ *
+ * @static
+ * @param  {p5.Vector} v1 the first p5.Vector
+ * @param  {p5.Vector} v2 the second p5.Vector
+ * @return {Number}     the distance
+ */
+p5.Vector.dist = function (v1,v2) {
+  return v1.dist(v2);
+};
+
+/**
+ * Linear interpolate a vector to another vector and return the result as a
+ * new vector.
+ *
+ * @static
+ * @param {p5.Vector} v1 a starting p5.Vector
+ * @param {p5.Vector} v2 the p5.Vector to lerp to
+ * @param {Number}       the amount of interpolation; some value between 0.0
+ *                       (old vector) and 1.0 (new vector). 0.1 is very near
+ *                       the new vector. 0.5 is halfway in between.
+ */
+p5.Vector.lerp = function (v1, v2, amt, target) {
+  if (!target) {
+    target = v1.copy();
+  } else {
+    target.set(v1);
+  }
+  target.lerp(v2, amt);
+  return target;
+};
+
+/**
+ * Calculates and returns the angle (in radians) between two vectors.
+ * @method angleBetween
+ * @static
+ * @param  {p5.Vector} v1 the x, y, and z components of a p5.Vector
+ * @param  {p5.Vector} v2 the x, y, and z components of a p5.Vector
+ * @return {Number}       the angle between (in radians)
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var angle = p5.Vector.angleBetween(v1, v2);
+ * // angle is PI/2
+ * </code>
+ * </div>
+ */
+p5.Vector.angleBetween = function (v1, v2) {
+  var angle = Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
+  if (this.p5) {
+    if (this.p5._angleMode === constants.DEGREES) {
+      angle = polarGeometry.radiansToDegrees(angle);
+    }
+  }
+  return angle;
+};
+
+module.exports = p5.Vector;
+
+},{"../core/constants":47,"../core/core":48,"./polargeometry":77}],77:[function(_dereq_,module,exports){
+
+module.exports = {
+
+  degreesToRadians: function(x) {
+    return 2 * Math.PI * x / 360;
+  },
+
+  radiansToDegrees: function(x) {
+    return 360 * x / (2 * Math.PI);
+  }
+
+};
+
+},{}],78:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Random
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+var seeded = false;
+
+// Linear Congruential Generator
+// Variant of a Lehman Generator
+var lcg = (function() {
+  // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
+  // m is basically chosen to be large (as it is the max period)
+  // and for its relationships to a and c
+  var m = 4294967296,
+    // a - 1 should be divisible by m's prime factors
+    a = 1664525,
+    // c and m should be co-prime
+    c = 1013904223,
+    seed, z;
+  return {
+    setSeed : function(val) {
+      // pick a random seed if val is undefined or null
+      // the >>> 0 casts the seed to an unsigned 32-bit integer
+      z = seed = (val == null ? Math.random() * m : val) >>> 0;
+    },
+    getSeed : function() {
+      return seed;
+    },
+    rand : function() {
+      // define the recurrence relationship
+      z = (a * z + c) % m;
+      // return a float in [0, 1)
+      // if z = m then z / m = 0 therefore (z % m) / m < 1 always
+      return z / m;
+    }
+  };
+}());
+
+/**
+ * Sets the seed value for random().
+ *
+ * By default, random() produces different results each time the program
+ * is run. Set the seed parameter to a constant to return the same
+ * pseudo-random numbers each time the software is run.
+ *
+ * @method randomSeed
+ * @param {Number} seed   the seed value
+ * @example
+ * <div>
+ * <code>
+ * randomSeed(99);
+ * for (var i=0; i < 100; i++) {
+ *   var r = random(0, 255);
+ *   stroke(r);
+ *   line(i, 0, i, 100);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.randomSeed = function(seed) {
+  lcg.setSeed(seed);
+  seeded = true;
+};
+
+/**
+ * Return a random number.
+ *
+ * Takes either 0, 1 or 2 arguments.
+ * If no argument is given, returns a random number between 0 and 1.
+ * If one argument is given, returns a random number between 0 and the number.
+ * If two arguments are given, returns a random number between them,
+ * inclusive.
+ *
+ * @method random
+ * @param  {Number} min   the lower bound
+ * @param  {Number} max   the upper bound
+ * @return {Number} the random number
+ * @example
+ * <div>
+ * <code>
+ * for (var i = 0; i < 100; i++) {
+ *   var r = random(50);
+ *   stroke(r*5);
+ *   line(50, i, 50+r, i);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * for (var i = 0; i < 100; i++) {
+ *   var r = random(-50, 50);
+ *   line(50,i,50+r,i);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * // Get a random element from an array
+ * var words = [ "apple", "bear", "cat", "dog" ];
+ * var index = floor(random(words.length));  // Convert to integer
+ * text(words[index],10,50);  // Displays one of the four words
+ * </code>
+ * </div>
+ */
+p5.prototype.random = function (min, max) {
+
+  var rand;
+
+  if (seeded) {
+    rand  = lcg.rand();
+  } else {
+    rand = Math.random();
+  }
+
+  if (arguments.length === 0) {
+    return rand;
+  } else
+  if (arguments.length === 1) {
+    return rand * min;
+  } else {
+    if (min > max) {
+      var tmp = min;
+      min = max;
+      max = tmp;
+    }
+
+    return rand * (max-min) + min;
+  }
+};
+
+
+/**
+ *
+ * Returns a random number fitting a Gaussian, or
+ * normal, distribution. There is theoretically no minimum or maximum
+ * value that randomGaussian() might return. Rather, there is
+ * just a very low probability that values far from the mean will be
+ * returned; and a higher probability that numbers near the mean will
+ * be returned.
+ * <br><br>
+ * Takes either 0, 1 or 2 arguments.<br>
+ * If no args, returns a mean of 0 and standard deviation of 1.<br>
+ * If one arg, that arg is the mean (standard deviation is 1).<br>
+ * If two args, first is mean, second is standard deviation.
+ *
+ * @method randomGaussian
+ * @param  {Number} mean  the mean
+ * @param  {Number} sd    the standard deviation
+ * @return {Number} the random number
+ * @example
+ * <div>
+ * <code>for (var y = 0; y < 100; y++) {
+ *  var x = randomGaussian(50,15);
+ *  line(50, y, x, y);
+ *}
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ *var distribution = new Array(360);
+ *
+ *function setup() {
+ *  createCanvas(100, 100);
+ *  for (var i = 0; i < distribution.length; i++) {
+ *    distribution[i] = floor(randomGaussian(0,15));
+ *  }
+ *}
+ *
+ *function draw() {
+ *  background(204);
+ *
+ *  translate(width/2, width/2);
+ *
+ *  for (var i = 0; i < distribution.length; i++) {
+ *    rotate(TWO_PI/distribution.length);
+ *    stroke(0);
+ *    var dist = abs(distribution[i]);
+ *    line(0, 0, dist, 0);
+ *  }
+ *}
+ * </code>
+ * </div>
+ */
+var y2;
+var previous = false;
+p5.prototype.randomGaussian = function(mean, sd)  {
+  var y1,x1,x2,w;
+  if (previous) {
+    y1 = y2;
+    previous = false;
+  } else {
+    do {
+      x1 = this.random(2) - 1;
+      x2 = this.random(2) - 1;
+      w = x1 * x1 + x2 * x2;
+    } while (w >= 1);
+    w = Math.sqrt((-2 * Math.log(w))/w);
+    y1 = x1 * w;
+    y2 = x2 * w;
+    previous = true;
+  }
+
+  var m = mean || 0;
+  var s = sd || 1;
+  return y1*s + m;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],79:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Trigonometry
+ * @for p5
+ * @requires core
+ * @requires polargeometry
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('./polargeometry');
+var constants = _dereq_('../core/constants');
+
+p5.prototype._angleMode = constants.RADIANS;
+
+/**
+ * The inverse of cos(), returns the arc cosine of a value. This function
+ * expects the values in the range of -1 to 1 and values are returned in
+ * the range 0 to PI (3.1415927).
+ *
+ * @method acos
+ * @param  {Number} value the value whose arc cosine is to be returned
+ * @return {Number}       the arc cosine of the given value
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var a = PI;
+ * var c = cos(a);
+ * var ac = acos(c);
+ * // Prints: "3.1415927 : -1.0 : 3.1415927"
+ * println(a + " : " + c + " : " +  ac);
+ * </code>
+ * </div>
+ *
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/4.0;
+ * var c = cos(a);
+ * var ac = acos(c);
+ * // Prints: "3.926991 : -0.70710665 : 2.3561943"
+ * println(a + " : " + c + " : " +  ac);
+ * </code>
+ * </div>
+ */
+p5.prototype.acos = function(ratio) {
+  if (this._angleMode === constants.RADIANS) {
+    return Math.acos(ratio);
+  } else {
+    return polarGeometry.radiansToDegrees(Math.acos(ratio));
+  }
+};
+
+/**
+ * The inverse of sin(), returns the arc sine of a value. This function
+ * expects the values in the range of -1 to 1 and values are returned
+ * in the range -PI/2 to PI/2.
+ *
+ * @method asin
+ * @param  {Number} value the value whose arc sine is to be returned
+ * @return {Number}       the arc sine of the given value
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3;
+ * var s = sin(a);
+ * var as = asin(s);
+ * // Prints: "1.0471976 : 0.86602545 : 1.0471976"
+ * println(a + " : " + s + " : " +  as);
+ * </code>
+ * </div>
+ *
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3.0;
+ * var s = sin(a);
+ * var as = asin(s);
+ * // Prints: "4.1887903 : -0.86602545 : -1.0471976"
+ * println(a + " : " + s + " : " +  as);
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.asin = function(ratio) {
+  if (this._angleMode === constants.RADIANS) {
+    return Math.asin(ratio);
+  } else {
+    return polarGeometry.radiansToDegrees(Math.asin(ratio));
+  }
+};
+
+/**
+ * The inverse of tan(), returns the arc tangent of a value. This function
+ * expects the values in the range of -Infinity to Infinity (exclusive) and
+ * values are returned in the range -PI/2 to PI/2.
+ *
+ * @method atan
+ * @param  {Number} value the value whose arc tangent is to be returned
+ * @return {Number}       the arc tangent of the given value
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3;
+ * var t = tan(a);
+ * var at = atan(t);
+ * // Prints: "1.0471976 : 1.7320509 : 1.0471976"
+ * println(a + " : " + t + " : " +  at);
+ * </code>
+ * </div>
+ *
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3.0;
+ * var t = tan(a);
+ * var at = atan(t);
+ * // Prints: "4.1887903 : 1.7320513 : 1.0471977"
+ * println(a + " : " + t + " : " +  at);
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.atan = function(ratio) {
+  if (this._angleMode === constants.RADIANS) {
+    return Math.atan(ratio);
+  } else {
+    return polarGeometry.radiansToDegrees(Math.atan(ratio));
+  }
+};
+
+/**
+ * Calculates the angle (in radians) from a specified point to the coordinate
+ * origin as measured from the positive x-axis. Values are returned as a
+ * float in the range from PI to -PI. The atan2() function is most often used
+ * for orienting geometry to the position of the cursor.
+ * <br><br>
+ * Note: The y-coordinate of the point is the first parameter, and the
+ * x-coordinate is the second parameter, due the the structure of calculating
+ * the tangent.
+ *
+ * @method atan2
+ * @param  {Number} y y-coordinate of the point
+ * @param  {Number} x x-coordinate of the point
+ * @return {Number}   the arc tangent of the given point
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ *   background(204);
+ *   translate(width/2, height/2);
+ *   var a = atan2(mouseY-height/2, mouseX-width/2);
+ *   rotate(a);
+ *   rect(-30, -5, 60, 10);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.atan2 = function (y, x) {
+  if (this._angleMode === constants.RADIANS) {
+    return Math.atan2(y, x);
+  } else {
+    return polarGeometry.radiansToDegrees(Math.atan2(y, x));
+  }
+};
+
+/**
+ * Calculates the cosine of an angle. This function takes into account the
+ * current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method cos
+ * @param  {Number} angle the angle
+ * @return {Number}       the cosine of the angle
+ *
+ * @example
+ * <div>
+ * <code>
+ * var a = 0.0;
+ * var inc = TWO_PI/25.0;
+ * for (var i = 0; i < 25; i++) {
+ *   line(i*4, 50, i*4, 50+cos(a)*40.0);
+ *   a = a + inc;
+ * }
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.cos = function(angle) {
+  if (this._angleMode === constants.RADIANS) {
+    return Math.cos(angle);
+  } else {
+    return Math.cos(this.radians(angle));
+  }
+};
+
+/**
+ * Calculates the sine of an angle. This function takes into account the
+ * current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method sin
+ * @param  {Number} angle the angle
+ * @return {Number}       the sine of the angle
+ *
+ * @example
+ * <div>
+ * <code>
+ * var a = 0.0;
+ * var inc = TWO_PI/25.0;
+ * for (var i = 0; i < 25; i++) {
+ *   line(i*4, 50, i*4, 50+sin(a)*40.0);
+ *   a = a + inc;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.sin = function(angle) {
+  if (this._angleMode === constants.RADIANS) {
+    return Math.sin(angle);
+  } else {
+    return Math.sin(this.radians(angle));
+  }
+};
+
+/**
+ * Calculates the tangent of an angle. This function takes into account
+ * the current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method tan
+ * @param  {Number} angle the angle
+ * @return {Number}       the tangent of the angle
+ *
+ * @example
+ * <div>
+ * <code>
+ *   var a = 0.0;
+ *   var inc = TWO_PI/50.0;
+ *   for (var i = 0; i < 100; i = i+2) {
+ *     line(i, 50, i, 50+tan(a)*2.0);
+ *     a = a + inc;
+ *   }
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.tan = function(angle) {
+  if (this._angleMode === constants.RADIANS) {
+    return Math.tan(angle);
+  } else {
+    return Math.tan(this.radians(angle));
+  }
+};
+
+/**
+ * Converts a radian measurement to its corresponding value in degrees.
+ * Radians and degrees are two ways of measuring the same thing. There are
+ * 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90° = PI/2 = 1.5707964.
+ *
+ * @method degrees
+ * @param  {Number} radians the radians value to convert to degrees
+ * @return {Number}         the converted angle
+ *
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var rad = PI/4;
+ * var deg = degrees(rad);
+ * println(rad + " radians is " + deg + " degrees");
+ * // Prints: 45 degrees is 0.7853981633974483 radians
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.degrees = function(angle) {
+  return polarGeometry.radiansToDegrees(angle);
+};
+
+/**
+ * Converts a degree measurement to its corresponding value in radians.
+ * Radians and degrees are two ways of measuring the same thing. There are
+ * 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90° = PI/2 = 1.5707964.
+ *
+ * @method radians
+ * @param  {Number} degrees the degree value to convert to radians
+ * @return {Number}         the converted angle
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var deg = 45.0;
+ * var rad = radians(deg);
+ * println(deg + " degrees is " + rad + " radians");
+ * // Prints: 45 degrees is 0.7853981633974483 radians
+ * </code>
+ * </div>
+ */
+p5.prototype.radians = function(angle) {
+  return polarGeometry.degreesToRadians(angle);
+};
+
+/**
+ * Sets the current mode of p5 to given mode. Default mode is RADIANS.
+ *
+ * @method angleMode
+ * @param {Number/Constant} mode either RADIANS or DEGREES
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw(){
+ *   background(204);
+ *   angleMode(DEGREES); // Change the mode to DEGREES
+ *   var a = atan2(mouseY-height/2, mouseX-width/2);
+ *   translate(width/2, height/2);
+ *   push();
+ *   rotate(a);
+ *   rect(-20, -5, 40, 10); // Larger rectangle is rotating in degrees
+ *   pop();
+ *   angleMode(RADIANS); // Change the mode to RADIANS
+ *   rotate(a); // var a stays the same
+ *   rect(-40, -5, 20, 10); // Smaller rectangle is rotating in radians
+ * }
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.angleMode = function(mode) {
+  if (mode === constants.DEGREES || mode === constants.RADIANS) {
+    this._angleMode = mode;
+  }
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"./polargeometry":77}],80:[function(_dereq_,module,exports){
+/**
+ * @module Typography
+ * @submodule Attributes
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Sets the current alignment for drawing text. The parameters LEFT, CENTER,
+ * and RIGHT set the alignment of text in relation to the values for
+ * the x and y parameters of the text() function.
+ *
+ * @method textAlign
+ * @param {Number/Constant} horizAlign horizontal alignment, either LEFT,
+ *                            CENTER, or RIGHT
+ * @param {Number/Constant} vertAlign vertical alignment, either TOP,
+ *                            BOTTOM, CENTER, or BASELINE
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * textSize(16);
+ * textAlign(RIGHT);
+ * text("ABCD", 50, 30);
+ * textAlign(CENTER);
+ * text("EFGH", 50, 50);
+ * textAlign(LEFT);
+ * text("IJKL", 50, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.textAlign = function(horizAlign, vertAlign) {
+  return this._renderer.textAlign.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the spacing, in pixels, between lines of text. This
+ * setting will be used in all subsequent calls to the text() function.
+ *
+ * @method textLeading
+ * @param {Number} leading the size in pixels for spacing between lines
+ * @return {Object|Number}
+ * @example
+ * <div>
+ * <code>
+ * // Text to display. The "\n" is a "new line" character
+ * lines = "L1\nL2\nL3";
+ * textSize(12);
+ *
+ * textLeading(10);  // Set leading to 10
+ * text(lines, 10, 25);
+ *
+ * textLeading(20);  // Set leading to 20
+ * text(lines, 40, 25);
+ *
+ * textLeading(30);  // Set leading to 30
+ * text(lines, 70, 25);
+ * </code>
+ * </div>
+ */
+p5.prototype.textLeading = function(theLeading) {
+  return this._renderer.textLeading.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the current font size. This size will be used in all subsequent
+ * calls to the text() function. Font size is measured in pixels.
+ *
+ * @method textSize
+ * @param {Number} theSize the size of the letters in units of pixels
+ * @return {Object|Number}
+ * @example
+ * <div>
+ * <code>
+ * textSize(12);
+ * text("Font Size 12", 10, 30);
+ * textSize(14);
+ * text("Font Size 14", 10, 60);
+ * textSize(16);
+ * text("Font Size 16", 10, 90);
+ * </code>
+ * </div>
+ */
+p5.prototype.textSize = function(theSize) {
+  return this._renderer.textSize.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the style of the text for system fonts to NORMAL, ITALIC, or BOLD.
+ * Note: this may be is overridden by CSS styling. For non-system fonts
+ * (opentype, truetype, etc.) please load styled fonts instead.
+ *
+ * @method textStyle
+ * @param {Number/Constant} theStyle styling for text, either NORMAL,
+ *                            ITALIC, or BOLD
+ * @return {Object|String}
+ * @example
+ * <div>
+ * <code>
+ * strokeWeight(0);
+ * textSize(12);
+ * textStyle(NORMAL);
+ * text("Font Style Normal", 10, 30);
+ * textStyle(ITALIC);
+ * text("Font Style Italic", 10, 60);
+ * textStyle(BOLD);
+ * text("Font Style Bold", 10, 90);
+ * </code>
+ * </div>
+ */
+p5.prototype.textStyle = function(theStyle) {
+  return this._renderer.textStyle.apply(this._renderer, arguments);
+};
+
+/**
+ * Calculates and returns the width of any character or text string.
+ *
+ * @method textWidth
+ * @param {String} theText the String of characters to measure
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * textSize(28);
+ *
+ * var aChar = 'P';
+ * var cWidth = textWidth(aChar);
+ * text(aChar, 0, 40);
+ * line(cWidth, 0, cWidth, 50);
+ *
+ * var aString = "p5.js";
+ * var sWidth = textWidth(aString);
+ * text(aString, 0, 85);
+ * line(sWidth, 50, sWidth, 100);
+ * </code>
+ * </div>
+ */
+p5.prototype.textWidth = function(theText) {
+  return this._renderer.textWidth.apply(this._renderer, arguments);
+};
+
+/**
+ * Returns the ascent of the current font at its current size. The ascent
+ * represents the distance, in pixels, of the tallest character above
+ * the baseline.
+ *
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * var base = height * 0.75;
+ * var scalar = 0.8; // Different for each font
+ *
+ * textSize(32);  // Set initial text size
+ * var asc = textAscent() * scalar;  // Calc ascent
+ * line(0, base - asc, width, base - asc);
+ * text("dp", 0, base);  // Draw text on baseline
+ *
+ * textSize(64);  // Increase text size
+ * asc = textAscent() * scalar;  // Recalc ascent
+ * line(40, base - asc, width, base - asc);
+ * text("dp", 40, base);  // Draw text on baseline
+ * </code>
+ * </div>
+ */
+p5.prototype.textAscent = function() {
+  return this._renderer.textAscent();
+};
+
+/**
+ * Returns the descent of the current font at its current size. The descent
+ * represents the distance, in pixels, of the character with the longest
+ * descender below the baseline.
+ *
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * var base = height * 0.75;
+ * var scalar = 0.8; // Different for each font
+ *
+ * textSize(32);  // Set initial text size
+ * var desc = textDescent() * scalar;  // Calc ascent
+ * line(0, base+desc, width, base+desc);
+ * text("dp", 0, base);  // Draw text on baseline
+ *
+ * textSize(64);  // Increase text size
+ * desc = textDescent() * scalar;  // Recalc ascent
+ * line(40, base + desc, width, base + desc);
+ * text("dp", 40, base);  // Draw text on baseline
+ * </code>
+ * </div>
+ */
+p5.prototype.textDescent = function() {
+  return this._renderer.textDescent();
+};
+
+/**
+ * Helper function to measure ascent and descent.
+ */
+p5.prototype._updateTextMetrics = function() {
+  return this._renderer._updateTextMetrics();
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],81:[function(_dereq_,module,exports){
+/**
+ * @module Typography
+ * @submodule Loading & Displaying
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+_dereq_('../core/error_helpers');
+
+
+/**
+ * Draws text to the screen. Displays the information specified in the first
+ * parameter on the screen in the position specified by the additional
+ * parameters. A default font will be used unless a font is set with the
+ * textFont() function and a default size will be used unless a font is set
+ * with textSize(). Change the color of the text with the fill() function.
+ * Change the outline of the text with the stroke() and strokeWeight()
+ * functions.
+ * <br><br>
+ * The text displays in relation to the textAlign() function, which gives the
+ * option to draw to the left, right, and center of the coordinates.
+ * <br><br>
+ * The x2 and y2 parameters define a rectangular area to display within and
+ * may only be used with string data. When these parameters are specified,
+ * they are interpreted based on the current rectMode() setting. Text that
+ * does not fit completely within the rectangle specified will not be drawn
+ * to the screen.
+ *
+ * @method text
+ * @param {String} str the alphanumeric symbols to be displayed
+ * @param {Number} x   x-coordinate of text
+ * @param {Number} y   y-coordinate of text
+ * @param {Number} x2  by default, the width of the text box,
+ *                     see rectMode() for more info
+ * @param {Number} y2  by default, the height of the text box,
+ *                     see rectMode() for more info
+ * @return {Object} this
+ * @example
+ * <div>
+ * <code>
+ * textSize(32);
+ * text("word", 10, 30);
+ * fill(0, 102, 153);
+ * text("word", 10, 60);
+ * fill(0, 102, 153, 51);
+ * text("word", 10, 90);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * s = "The quick brown fox jumped over the lazy dog.";
+ * fill(50);
+ * text(s, 10, 10, 70, 80); // Text wraps within text box
+ * </code>
+ * </div>
+ */
+p5.prototype.text = function(str, x, y, maxWidth, maxHeight) {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  this._validateParameters(
+    'text',
+    args,
+    [
+      ['*', 'Number', 'Number'],
+      ['*', 'Number', 'Number', 'Number', 'Number']
+    ]
+  );
+
+  return (!(this._renderer._doFill || this._renderer._doStroke)) ? this :
+    this._renderer.text.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets the current font that will be drawn with the text() function.
+ *
+ * @method textFont
+ * @param {Object|String} f a font loaded via loadFont(), or a String
+ *  representing a browser-based dfault font.
+ * @return {Object} this
+ * @example
+ * <div>
+ * <code>
+ * fill(0);
+ * textSize(12);
+ * textFont("Georgia");
+ * text("Georgia", 12, 30);
+ * textFont("Helvetica");
+ * text("Helvetica", 12, 60);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * var fontRegular, fontItalic, fontBold;
+ * function preload() {
+ *    fontRegular = loadFont("assets/Regular.otf");
+ *    fontItalic = loadFont("assets/Italic.ttf");
+ *    fontBold = loadFont("assets/Bold.ttf");
+ * }
+ * function setup() {
+ *    background(210);
+ *    fill(0).strokeWeight(0).textSize(10);
+ *    textFont(fontRegular);
+ *    text("Font Style Normal", 10, 30);
+ *    textFont(fontItalic);
+ *    text("Font Style Italic", 10, 50);
+ *    textFont(fontBold);
+ *    text("Font Style Bold", 10, 70);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.textFont = function(theFont, theSize) {
+
+  if (arguments.length) {
+
+    if (!theFont) {
+
+      throw Error('null font passed to textFont');
+    }
+
+    this._renderer._setProperty('_textFont', theFont);
+
+    if (theSize) {
+
+      this._renderer._setProperty('_textSize', theSize);
+      this._renderer._setProperty('_textLeading',
+        theSize * constants._DEFAULT_LEADMULT);
+    }
+
+    return this._renderer._applyTextProperties();
+  }
+
+  return this;
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"../core/error_helpers":51}],82:[function(_dereq_,module,exports){
+/**
+ * This module defines the p5.Font class and functions for
+ * drawing text to the display canvas.
+ * @module Typography
+ * @submodule Font
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+/*
+ * TODO:
+ *
+ * API:
+ * -- textBounds()
+ * -- getPath()
+ * -- getPoints()
+ *
+ * ===========================================
+ * -- PFont functions:
+ *    PFont.list()
+ *
+ * -- kerning
+ * -- alignment: justified?
+ * -- integrate p5.dom? (later)
+ */
+
+/**
+ * Base class for font handling
+ * @class p5.Font
+ * @constructor
+ * @param {Object} [pInst] pointer to p5 instance
+ */
+p5.Font = function(p) {
+
+  this.parent = p;
+
+  this.cache = {};
+
+  /**
+   * Underlying opentype font implementation
+   * @property font
+   */
+  this.font = undefined;
+};
+
+p5.Font.prototype.list = function() {
+
+  // TODO
+  throw 'not yet implemented';
+};
+
+/**
+ * Returns a tight bounding box for the given text string using this
+ * font (currently only supports single lines)
+ *
+ * @method textBounds
+ * @param  {String} line     a line of text
+ * @param  {Number} x        x-position
+ * @param  {Number} y        y-position
+ * @param  {Number} fontSize font size to use (optional)
+ * @param  {Object} options opentype options (optional)
+ *
+ * @return {Object}          a rectangle object with properties: x, y, w, h
+ *
+ * @example
+ * <div>
+ * <code>
+ * var font;
+ * var textString = 'Lorem ipsum dolor sit amet.';
+ * function preload() {
+ *    font = loadFont('./assets/Regular.otf');
+ * };
+ * function setup() {
+ *    background(210);
+ *
+ *    var bbox = font.textBounds(textString, 10, 30, 12);
+ *    fill(255);
+ *    stroke(0);
+ *    rect(bbox.x, bbox.y, bbox.w, bbox.h);
+ *    fill(0);
+ *    noStroke();
+ *
+ *    textFont(font);
+ *    textSize(12);
+ *    text(textString, 10, 30);
+ * };
+ * </code>
+ * </div>
+ */
+p5.Font.prototype.textBounds = function(str, x, y, fontSize, options) {
+
+  x = x !== undefined ? x : 0;
+  y = y !== undefined ? y : 0;
+  fontSize = fontSize || this.parent._renderer._textSize;
+
+  var result = this.cache[cacheKey('textBounds', str, x, y, fontSize)];
+  if (!result) {
+
+    var xCoords = [], yCoords = [], self = this,
+      scale = this._scale(fontSize), minX, minY, maxX, maxY;
+
+    this.font.forEachGlyph(str, x, y, fontSize, options,
+      function(glyph, gX, gY, gFontSize) {
+
+        xCoords.push(gX);
+        yCoords.push(gY);
+
+        var gm = glyph.getMetrics();
+
+        if (glyph.name !== 'space') {
+
+          xCoords.push(gX + (gm.xMax * scale));
+          yCoords.push(gY + (-gm.yMin * scale));
+          yCoords.push(gY + (-gm.yMax * scale));
+
+        } else { // NOTE: deals with broken metrics for spaces in opentype.js
+
+          xCoords.push(gX + self.font.charToGlyph(' ').advanceWidth *
+            self._scale(fontSize));
+        }
+      });
+
+    minX = Math.max(0, Math.min.apply(null, xCoords));
+    minY = Math.max(0, Math.min.apply(null, yCoords));
+    maxX = Math.max(0, Math.max.apply(null, xCoords));
+    maxY = Math.max(0, Math.max.apply(null, yCoords));
+
+    result = {
+      x: minX,
+      y: minY,
+      h: maxY - minY,
+      w: maxX - minX,
+      advance: minX - x
+    };
+
+    this.cache[cacheKey('textBounds', str, x, y, fontSize)] = result;
+  }
+  //else console.log('cache-hit');
+
+  return result;
+};
+
+
+/**
+ * Computes an array of points following the path for specified text
+ *
+ * @param  {String} txt     a line of text
+ * @param  {Number} x        x-position
+ * @param  {Number} y        y-position
+ * @param  {Number} fontSize font size to use (optional)
+ * @param  {Object} options  an (optional) object that can contain:
+ *
+ * <br>sampleFactor - the ratio of path-length to number of samples
+ * (default=.25); higher values yield more points and are therefore
+ * more precise
+ *
+ * <br>simplifyThreshold - if set to a non-zero value, collinear points will be
+ * be removed from the polygon; the value represents the threshold angle to use
+ * when determining whether two edges are collinear
+ *
+ * @return {Array}  an array of points, each with x, y, alpha (the path angle)
+ */
+p5.Font.prototype.textToPoints = function(txt, x, y, fontSize, options) {
+
+  var xoff = 0, result = [], glyphs = this._getGlyphs(txt);
+
+  fontSize = fontSize || this.parent._renderer._textSize;
+
+  for (var i = 0; i < glyphs.length; i++) {
+
+    var gpath = glyphs[i].getPath(x, y, fontSize),
+      paths = splitPaths(gpath.commands);
+
+    for (var j = 0; j < paths.length; j++) {
+
+      var pts = pathToPoints(paths[j], options);
+
+      for (var k = 0; k < pts.length; k++) {
+        pts[k].x += xoff;
+        result.push(pts[k]);
+      }
+    }
+
+    xoff += glyphs[i].advanceWidth * this._scale(fontSize);
+  }
+
+  return result;
+};
+
+// ----------------------------- End API ------------------------------
+
+/**
+ * Returns the set of opentype glyphs for the supplied string.
+ *
+ * Note that there is not a strict one-to-one mapping between characters
+ * and glyphs, so the list of returned glyphs can be larger or smaller
+ *  than the length of the given string.
+ *
+ * @param  {String} str the string to be converted
+ * @return {array}     the opentype glyphs
+ */
+p5.Font.prototype._getGlyphs = function(str) {
+
+  return this.font.stringToGlyphs(str);
+};
+
+/**
+ * Returns an opentype path for the supplied string and position.
+ *
+ * @param  {String} line     a line of text
+ * @param  {Number} x        x-position
+ * @param  {Number} y        y-position
+ * @param  {Object} options opentype options (optional)
+ * @return {Object}     the opentype path
+ */
+p5.Font.prototype._getPath = function(line, x, y, options) {
+
+  var p = this.parent,
+    ctx = p._renderer.drawingContext,
+    pos = this._handleAlignment(p, ctx, line, x, y);
+
+  return this.font.getPath(line, pos.x, pos.y, p._renderer._textSize, options);
+};
+
+/*
+ * Creates an SVG-formatted path-data string
+ * (See http://www.w3.org/TR/SVG/paths.html#PathData)
+ * from the given opentype path or string/position
+ *
+ * @param  {Object} path    an opentype path, OR the following:
+ *
+ * @param  {String} line     a line of text
+ * @param  {Number} x        x-position
+ * @param  {Number} y        y-position
+ * @param  {Object} options opentype options (optional), set options.decimals
+ * to set the decimal precision of the path-data
+ *
+ * @return {Object}     this p5.Font object
+ */
+p5.Font.prototype._getPathData = function(line, x, y, options) {
+
+  var decimals = 3;
+
+  // create path from string/position
+  if (typeof line === 'string' && arguments.length > 2) {
+
+    line = this._getPath(line, x, y, options);
+  }
+  // handle options specified in 2nd arg
+  else if (typeof x === 'object') {
+
+    options = x;
+  }
+
+  // handle svg arguments
+  if (options && typeof options.decimals === 'number') {
+
+    decimals = options.decimals;
+  }
+
+  return line.toPathData(decimals);
+};
+
+/*
+ * Creates an SVG <path> element, as a string,
+ * from the given opentype path or string/position
+ *
+ * @param  {Object} path    an opentype path, OR the following:
+ *
+ * @param  {String} line     a line of text
+ * @param  {Number} x        x-position
+ * @param  {Number} y        y-position
+ * @param  {Object} options opentype options (optional), set options.decimals
+ * to set the decimal precision of the path-data in the <path> element,
+ *  options.fill to set the fill color for the <path> element,
+ *  options.stroke to set the stroke color for the <path> element,
+ *  options.strokeWidth to set the strokeWidth for the <path> element.
+ *
+ * @return {Object}     this p5.Font object
+ */
+p5.Font.prototype._getSVG = function(line, x, y, options) {
+
+  var decimals = 3;
+
+  // create path from string/position
+  if (typeof line === 'string' && arguments.length > 2) {
+
+    line = this._getPath(line, x, y, options);
+  }
+  // handle options specified in 2nd arg
+  else if (typeof x === 'object') {
+
+    options = x;
+  }
+
+  // handle svg arguments
+  if (options) {
+    if (typeof options.decimals === 'number') {
+      decimals = options.decimals;
+    }
+    if (typeof options.strokeWidth === 'number') {
+      line.strokeWidth = options.strokeWidth;
+    }
+    if (typeof options.fill !== 'undefined') {
+      line.fill = options.fill;
+    }
+    if (typeof options.stroke !== 'undefined') {
+      line.stroke = options.stroke;
+    }
+  }
+
+  return line.toSVG(decimals);
+};
+
+/*
+ * Renders an opentype path or string/position
+ * to the current graphics context
+ *
+ * @param  {Object} path    an opentype path, OR the following:
+ *
+ * @param  {String} line     a line of text
+ * @param  {Number} x        x-position
+ * @param  {Number} y        y-position
+ * @param  {Object} options opentype options (optional)
+ *
+ * @return {Object}     this p5.Font object
+ */
+p5.Font.prototype._renderPath = function(line, x, y, options) {
+
+  var pdata, pg = (options && options.renderer) || this.parent._renderer,
+    ctx = pg.drawingContext;
+
+  if (typeof line === 'object' && line.commands) {
+
+    pdata = line.commands;
+  } else {
+
+    //pos = handleAlignment(p, ctx, line, x, y);
+    pdata = this._getPath(line, x, y, pg._textSize, options).commands;
+  }
+
+  ctx.beginPath();
+  for (var i = 0; i < pdata.length; i += 1) {
+
+    var cmd = pdata[i];
+    if (cmd.type === 'M') {
+      ctx.moveTo(cmd.x, cmd.y);
+    } else if (cmd.type === 'L') {
+      ctx.lineTo(cmd.x, cmd.y);
+    } else if (cmd.type === 'C') {
+      ctx.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+    } else if (cmd.type === 'Q') {
+      ctx.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y);
+    } else if (cmd.type === 'Z') {
+      ctx.closePath();
+    }
+  }
+
+  // only draw stroke if manually set by user
+  if (pg._doStroke && pg._strokeSet) {
+
+    ctx.stroke();
+  }
+
+  if (pg._doFill) {
+
+    // if fill hasn't been set by user, use default-text-fill
+    ctx.fillStyle = pg._fillSet ? ctx.fillStyle : constants._DEFAULT_TEXT_FILL;
+    ctx.fill();
+  }
+
+  return this;
+};
+
+p5.Font.prototype._textWidth = function(str, fontSize) {
+
+  if (str === ' ') { // special case for now
+
+    return this.font.charToGlyph(' ').advanceWidth * this._scale(fontSize);
+  }
+
+  var bounds = this.textBounds(str, 0, 0, fontSize);
+  return bounds.w + bounds.advance;
+};
+
+p5.Font.prototype._textAscent = function(fontSize) {
+
+  return this.font.ascender * this._scale(fontSize);
+};
+
+p5.Font.prototype._textDescent = function(fontSize) {
+
+  return -this.font.descender * this._scale(fontSize);
+};
+
+p5.Font.prototype._scale = function(fontSize) {
+
+  return (1 / this.font.unitsPerEm) * (fontSize ||
+    this.parent._renderer._textSize);
+};
+
+p5.Font.prototype._handleAlignment = function(p, ctx, line, x, y) {
+
+  var textWidth = this._textWidth(line),
+    textAscent = this._textAscent(),
+    textDescent = this._textDescent(),
+    textHeight = textAscent + textDescent;
+
+  if (ctx.textAlign === constants.CENTER) {
+    x -= textWidth / 2;
+  } else if (ctx.textAlign === constants.RIGHT) {
+    x -= textWidth;
+  }
+
+  if (ctx.textBaseline === constants.TOP) {
+    y += textHeight;
+  } else if (ctx.textBaseline === constants._CTX_MIDDLE) {
+    y += textHeight / 2 - textDescent;
+  } else if (ctx.textBaseline === constants.BOTTOM) {
+    y -= textDescent;
+  }
+
+  return { x: x, y: y };
+};
+
+// path-utils
+
+function pathToPoints(cmds, options) {
+
+  var opts = parseOpts(options, {
+    sampleFactor: 0.1,
+    simplifyThreshold: 0,
+  });
+
+  var len = pointAtLength(cmds,0,1), // total-length
+    t = len / (len * opts.sampleFactor),
+    pts = [];
+
+  for (var i = 0; i < len; i += t) {
+    pts.push(pointAtLength(cmds, i));
+  }
+
+  if (opts.simplifyThreshold) {
+    /*var count = */simplify(pts, opts.simplifyThreshold);
+    //console.log('Simplify: removed ' + count + ' pts');
+  }
+
+  return pts;
+}
+
+function simplify(pts, angle) {
+
+  angle = (typeof angle === 'undefined') ? 0 : angle;
+
+  var num = 0;
+  for (var i = pts.length - 1; pts.length > 3 && i >= 0; --i) {
+
+    if (collinear(at(pts, i - 1), at(pts, i), at(pts, i + 1), angle)) {
+
+      // Remove the middle point
+      pts.splice(i % pts.length, 1);
+      num++;
+    }
+  }
+  return num;
+}
+
+function splitPaths(cmds) {
+
+  var paths = [], current;
+  for (var i = 0; i < cmds.length; i++) {
+    if (cmds[i].type === 'M') {
+      if (current) {
+        paths.push(current);
+      }
+      current = [];
+    }
+    current.push(cmdToArr(cmds[i]));
+  }
+  paths.push(current);
+
+  return paths;
+}
+
+function cmdToArr(cmd) {
+
+  var arr = [ cmd.type ];
+  if (cmd.type === 'M' || cmd.type === 'L') { // moveto or lineto
+    arr.push(cmd.x, cmd.y);
+  } else if (cmd.type === 'C') {
+    arr.push(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+  } else if (cmd.type === 'Q') {
+    arr.push(cmd.x1, cmd.y1, cmd.x, cmd.y);
+  }
+  // else if (cmd.type === 'Z') { /* no-op */ }
+  return arr;
+}
+
+function parseOpts(options, defaults) {
+
+  if (typeof options !== 'object') {
+    options = defaults;
+  }
+  else {
+    for (var key in defaults) {
+      if (typeof options[key] === 'undefined') {
+        options[key] = defaults[key];
+      }
+    }
+  }
+  return options;
+}
+
+//////////////////////// Helpers ////////////////////////////
+
+function at(v, i) {
+  var s = v.length;
+  return v[i < 0 ? i % s + s : i % s];
+}
+
+function collinear(a, b, c, thresholdAngle) {
+
+  if (!thresholdAngle) {
+    return areaTriangle(a, b, c) === 0;
+  }
+
+  if (typeof collinear.tmpPoint1 === 'undefined') {
+    collinear.tmpPoint1 = [];
+    collinear.tmpPoint2 = [];
+  }
+
+  var ab = collinear.tmpPoint1, bc = collinear.tmpPoint2;
+  ab.x = b.x - a.x;
+  ab.y = b.y - a.y;
+  bc.x = c.x - b.x;
+  bc.y = c.y - b.y;
+
+  var dot = ab.x * bc.x + ab.y * bc.y,
+    magA = Math.sqrt(ab.x * ab.x + ab.y * ab.y),
+    magB = Math.sqrt(bc.x * bc.x + bc.y * bc.y),
+    angle = Math.acos(dot / (magA * magB));
+
+  return angle < thresholdAngle;
+}
+
+function areaTriangle(a, b, c) {
+  return (((b[0] - a[0]) * (c[1] - a[1])) - ((c[0] - a[0]) * (b[1] - a[1])));
+}
+
+// Portions of below code copyright 2008 Dmitry Baranovskiy (via MIT license)
+
+function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+
+  var t1 = 1 - t, t13 = Math.pow(t1, 3), t12 = Math.pow(t1, 2), t2 = t * t,
+    t3 = t2 * t, x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x +
+    t3 * p2x, y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y +
+    t3 * p2y, mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
+    my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
+    nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
+    ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
+    ax = t1 * p1x + t * c1x, ay = t1 * p1y + t * c1y,
+    cx = t1 * c2x + t * p2x, cy = t1 * c2y + t * p2y,
+    alpha = (90 - Math.atan2(mx - nx, my - ny) * 180 / Math.PI);
+
+  if (mx > nx || my < ny) { alpha += 180; }
+
+  return { x: x, y: y, m: { x: mx, y: my }, n: { x: nx, y: ny },
+    start: { x: ax, y: ay }, end: { x: cx, y: cy }, alpha: alpha
+  };
+}
+
+function getPointAtSegmentLength(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,length) {
+  return (length == null) ? bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) :
+    findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y,
+      getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
+}
+
+function pointAtLength(path, length, istotal) {
+  path = path2curve(path);
+  var x, y, p, l, sp = '', subpaths = {}, point, len = 0;
+  for (var i = 0, ii = path.length; i < ii; i++) {
+    p = path[i];
+    if (p[0] === 'M') {
+      x = +p[1];
+      y = +p[2];
+    } else {
+      l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+      if (len + l > length) {
+        if (!istotal) {
+          point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5],
+            p[6], length - len);
+          return { x: point.x, y: point.y, alpha: point.alpha };
+        }
+      }
+      len += l;
+      x = +p[5];
+      y = +p[6];
+    }
+    sp += p.shift() + p;
+  }
+  subpaths.end = sp;
+
+  point = istotal ? len : findDotsAtSegment
+    (x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
+
+  if (point.alpha) {
+    point = { x: point.x, y: point.y, alpha: point.alpha };
+  }
+
+  return point;
+}
+
+function pathToAbsolute(pathArray) {
+
+  var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0;
+  if (pathArray[0][0] === 'M') {
+    x = +pathArray[0][1];
+    y = +pathArray[0][2];
+    mx = x;
+    my = y;
+    start++;
+    res[0] = ['M', x, y];
+  }
+
+  var dots,crz = pathArray.length===3 && pathArray[0][0]==='M' &&
+    pathArray[1][0].toUpperCase()==='R' && pathArray[2][0].toUpperCase()==='Z';
+
+  for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
+    res.push(r = []);
+    pa = pathArray[i];
+    if (pa[0] !== String.prototype.toUpperCase.call(pa[0])) {
+      r[0] = String.prototype.toUpperCase.call(pa[0]);
+      switch (r[0]) {
+        case 'A':
+          r[1] = pa[1];
+          r[2] = pa[2];
+          r[3] = pa[3];
+          r[4] = pa[4];
+          r[5] = pa[5];
+          r[6] = +(pa[6] + x);
+          r[7] = +(pa[7] + y);
+          break;
+        case 'V':
+          r[1] = +pa[1] + y;
+          break;
+        case 'H':
+          r[1] = +pa[1] + x;
+          break;
+        case 'R':
+          dots = [x, y].concat(pa.slice(1));
+          for (var j = 2, jj = dots.length; j < jj; j++) {
+            dots[j] = +dots[j] + x;
+            dots[++j] = +dots[j] + y;
+          }
+          res.pop();
+          res = res.concat(catmullRom2bezier(dots, crz));
+          break;
+        case 'M':
+          mx = +pa[1] + x;
+          my = +pa[2] + y;
+          break;
+        default:
+          for (j = 1, jj = pa.length; j < jj; j++) {
+            r[j] = +pa[j] + ((j % 2) ? x : y);
+          }
+      }
+    } else if (pa[0] === 'R') {
+      dots = [x, y].concat(pa.slice(1));
+      res.pop();
+      res = res.concat(catmullRom2bezier(dots, crz));
+      r = ['R'].concat(pa.slice(-2));
+    } else {
+      for (var k = 0, kk = pa.length; k < kk; k++) {
+        r[k] = pa[k];
+      }
+    }
+    switch (r[0]) {
+      case 'Z':
+        x = mx;
+        y = my;
+        break;
+      case 'H':
+        x = r[1];
+        break;
+      case 'V':
+        y = r[1];
+        break;
+      case 'M':
+        mx = r[r.length - 2];
+        my = r[r.length - 1];
+        break;
+      default:
+        x = r[r.length - 2];
+        y = r[r.length - 1];
+    }
+  }
+  return res;
+}
+
+function path2curve(path, path2) {
+
+  var p = pathToAbsolute(path), p2 = path2 && pathToAbsolute(path2),
+    attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+    attrs2 = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+
+    processPath = function(path, d, pcom) {
+      var nx, ny, tq = { T: 1, Q: 1 };
+      if (!path) { return ['C', d.x, d.y, d.x, d.y, d.x, d.y]; }
+      if (!(path[0] in tq)) { d.qx = d.qy = null; }
+      switch (path[0]) {
+        case 'M':
+          d.X = path[1];
+          d.Y = path[2];
+          break;
+        case 'A':
+          path = ['C'].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))));
+          break;
+        case 'S':
+          if (pcom === 'C' || pcom === 'S') {
+            nx = d.x * 2 - d.bx;
+            ny = d.y * 2 - d.by;
+          } else {
+            nx = d.x;
+            ny = d.y;
+          }
+          path = ['C', nx, ny].concat(path.slice(1));
+          break;
+        case 'T':
+          if (pcom === 'Q' || pcom === 'T') {
+            d.qx = d.x * 2 - d.qx;
+            d.qy = d.y * 2 - d.qy;
+          } else {
+            d.qx = d.x;
+            d.qy = d.y;
+          }
+          path = ['C'].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
+          break;
+        case 'Q':
+          d.qx = path[1];
+          d.qy = path[2];
+          path = ['C'].concat(q2c(d.x,d.y,path[1],path[2],path[3],path[4]));
+          break;
+        case 'L':
+          path = ['C'].concat(l2c(d.x, d.y, path[1], path[2]));
+          break;
+        case 'H':
+          path = ['C'].concat(l2c(d.x, d.y, path[1], d.y));
+          break;
+        case 'V':
+          path = ['C'].concat(l2c(d.x, d.y, d.x, path[1]));
+          break;
+        case 'Z':
+          path = ['C'].concat(l2c(d.x, d.y, d.X, d.Y));
+          break;
+      }
+      return path;
+    },
+
+    fixArc = function(pp, i) {
+      if (pp[i].length > 7) {
+        pp[i].shift();
+        var pi = pp[i];
+        while (pi.length) {
+          pcoms1[i] = 'A';
+          if (p2) { pcoms2[i] = 'A'; }
+          pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)));
+        }
+        pp.splice(i, 1);
+        ii = Math.max(p.length, p2 && p2.length || 0);
+      }
+    },
+
+    fixM = function(path1, path2, a1, a2, i) {
+      if (path1 && path2 && path1[i][0] === 'M' && path2[i][0] !== 'M') {
+        path2.splice(i, 0, ['M', a2.x, a2.y]);
+        a1.bx = 0;
+        a1.by = 0;
+        a1.x = path1[i][1];
+        a1.y = path1[i][2];
+        ii = Math.max(p.length, p2 && p2.length || 0);
+      }
+    },
+
+    pcoms1 = [], // path commands of original path p
+    pcoms2 = [], // path commands of original path p2
+    pfirst = '', // temporary holder for original path command
+    pcom = ''; // holder for previous path command of original path
+
+  for (var i = 0, ii = Math.max(p.length, p2 && p2.length || 0); i < ii; i++) {
+    if (p[i]) { pfirst = p[i][0]; } // save current path command
+
+    if (pfirst !== 'C') {
+      pcoms1[i] = pfirst; // Save current path command
+      if (i) { pcom = pcoms1[i - 1]; } // Get previous path command pcom
+    }
+    p[i] = processPath(p[i], attrs, pcom);
+
+    if (pcoms1[i] !== 'A' && pfirst === 'C') { pcoms1[i] = 'C'; }
+
+    fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1
+
+    if (p2) { // the same procedures is done to p2
+      if (p2[i]) { pfirst = p2[i][0]; }
+      if (pfirst !== 'C') {
+        pcoms2[i] = pfirst;
+        if (i) { pcom = pcoms2[i - 1]; }
+      }
+      p2[i] = processPath(p2[i], attrs2, pcom);
+
+      if (pcoms2[i] !== 'A' && pfirst === 'C') { pcoms2[i] = 'C'; }
+
+      fixArc(p2, i);
+    }
+    fixM(p, p2, attrs, attrs2, i);
+    fixM(p2, p, attrs2, attrs, i);
+    var seg = p[i], seg2 = p2 && p2[i], seglen = seg.length,
+      seg2len = p2 && seg2.length;
+    attrs.x = seg[seglen - 2];
+    attrs.y = seg[seglen - 1];
+    attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
+    attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
+    attrs2.bx = p2 && (parseFloat(seg2[seg2len - 4]) || attrs2.x);
+    attrs2.by = p2 && (parseFloat(seg2[seg2len - 3]) || attrs2.y);
+    attrs2.x = p2 && seg2[seg2len - 2];
+    attrs2.y = p2 && seg2[seg2len - 1];
+  }
+
+  return p2 ? [p, p2] : p;
+}
+
+function a2c(x1, y1, rx, ry, angle, lac, sweep_flag, x2, y2, recursive) {
+  // for more information of where this Math came from visit:
+  // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+  var PI = Math.PI, _120 = PI * 120 / 180, f1, f2, cx, cy,
+    rad = PI / 180 * (+angle || 0), res = [], xy,
+    rotate = function (x, y, rad) {
+      var X = x * Math.cos(rad) - y * Math.sin(rad),
+        Y = x * Math.sin(rad) + y * Math.cos(rad);
+      return { x: X, y: Y };
+    };
+  if (!recursive) {
+    xy = rotate(x1, y1, -rad);
+    x1 = xy.x;
+    y1 = xy.y;
+    xy = rotate(x2, y2, -rad);
+    x2 = xy.x;
+    y2 = xy.y;
+    var x = (x1 - x2) / 2, y = (y1 - y2) / 2,
+      h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+    if (h > 1) {
+      h = Math.sqrt(h);
+      rx = h * rx;
+      ry = h * ry;
+    }
+    var rx2 = rx * rx, ry2 = ry * ry,
+      k = (lac === sweep_flag ? -1 : 1) * Math.sqrt(Math.abs
+        ((rx2 * ry2 - rx2 * y * y - ry2 * x * x)/(rx2 * y * y + ry2 * x * x)));
+
+    cx = k * rx * y / ry + (x1 + x2) / 2;
+    cy = k * -ry * x / rx + (y1 + y2) / 2;
+    f1 = Math.asin(((y1 - cy) / ry).toFixed(9));
+    f2 = Math.asin(((y2 - cy) / ry).toFixed(9));
+
+    f1 = x1 < cx ? PI - f1 : f1;
+    f2 = x2 < cx ? PI - f2 : f2;
+
+    if (f1 < 0) { f1 = PI * 2 + f1; }
+    if (f2 < 0) { f2 = PI * 2 + f2; }
+
+    if (sweep_flag && f1 > f2) {
+      f1 = f1 - PI * 2;
+    }
+    if (!sweep_flag && f2 > f1) {
+      f2 = f2 - PI * 2;
+    }
+  } else {
+    f1 = recursive[0];
+    f2 = recursive[1];
+    cx = recursive[2];
+    cy = recursive[3];
+  }
+  var df = f2 - f1;
+  if (Math.abs(df) > _120) {
+    var f2old = f2, x2old = x2, y2old = y2;
+    f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+    x2 = cx + rx * Math.cos(f2);
+    y2 = cy + ry * Math.sin(f2);
+    res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old,
+      [f2, f2old, cx, cy]);
+  }
+  df = f2 - f1;
+  var c1 = Math.cos(f1),
+    s1 = Math.sin(f1),
+    c2 = Math.cos(f2),
+    s2 = Math.sin(f2),
+    t = Math.tan(df / 4),
+    hx = 4 / 3 * rx * t,
+    hy = 4 / 3 * ry * t,
+    m1 = [x1, y1],
+    m2 = [x1 + hx * s1, y1 - hy * c1],
+    m3 = [x2 + hx * s2, y2 - hy * c2],
+    m4 = [x2, y2];
+  m2[0] = 2 * m1[0] - m2[0];
+  m2[1] = 2 * m1[1] - m2[1];
+  if (recursive) {
+    return [m2, m3, m4].concat(res);
+  } else {
+    res = [m2, m3, m4].concat(res).join().split(',');
+    var newres = [];
+    for (var i = 0, ii = res.length; i < ii; i++) {
+      newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i],
+        res[i + 1], rad).x;
+    }
+    return newres;
+  }
+}
+
+// http://schepers.cc/getting-to-the-point
+function catmullRom2bezier(crp, z) {
+  var d = [];
+  for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
+    var p = [{
+      x: +crp[i - 2],
+      y: +crp[i - 1]
+    }, {
+      x: +crp[i],
+      y: +crp[i + 1]
+    }, {
+      x: +crp[i + 2],
+      y: +crp[i + 3]
+    }, {
+      x: +crp[i + 4],
+      y: +crp[i + 5]
+    }];
+    if (z) {
+      if (!i) {
+        p[0] = {
+          x: +crp[iLen - 2],
+          y: +crp[iLen - 1]
+        };
+      } else if (iLen - 4 === i) {
+        p[3] = {
+          x: +crp[0],
+          y: +crp[1]
+        };
+      } else if (iLen - 2 === i) {
+        p[2] = {
+          x: +crp[0],
+          y: +crp[1]
+        };
+        p[3] = {
+          x: +crp[2],
+          y: +crp[3]
+        };
+      }
+    } else {
+      if (iLen - 4 === i) {
+        p[3] = p[2];
+      } else if (!i) {
+        p[0] = {
+          x: +crp[i],
+          y: +crp[i + 1]
+        };
+      }
+    }
+    d.push(['C', (-p[0].x + 6 * p[1].x + p[2].x) / 6, (-p[0].y + 6 * p[1].y +
+      p[2].y) / 6, (p[1].x + 6 * p[2].x - p[3].x) / 6, (p[1].y + 6 * p[2].y -
+      p[3].y) / 6, p[2].x, p[2].y ]);
+  }
+
+  return d;
+}
+
+function l2c(x1, y1, x2, y2) { return [x1, y1, x2, y2, x2, y2]; }
+
+function q2c(x1, y1, ax, ay, x2, y2) {
+  var _13 = 1 / 3, _23 = 2 / 3;
+  return [
+    _13 * x1 + _23 * ax, _13 * y1 + _23 * ay,
+    _13 * x2 + _23 * ax, _13 * y2 + _23 * ay, x2, y2
+  ];
+}
+
+function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
+  if (z == null) { z = 1; }
+  z = z > 1 ? 1 : z < 0 ? 0 : z;
+  var z2 = z / 2,
+    n = 12, Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873,
+       -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816],
+    sum = 0, Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032,
+      0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472 ];
+  for (var i = 0; i < n; i++) {
+    var ct = z2 * Tvalues[i] + z2,
+      xbase = base3(ct, x1, x2, x3, x4),
+      ybase = base3(ct, y1, y2, y3, y4),
+      comb = xbase * xbase + ybase * ybase;
+    sum += Cvalues[i] * Math.sqrt(comb);
+  }
+  return z2 * sum;
+}
+
+function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
+  if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
+    return;
+  }
+  var t = 1, step = t / 2, t2 = t - step, l, e = 0.01;
+  l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+  while (Math.abs(l - ll) > e) {
+    step /= 2;
+    t2 += (l < ll ? 1 : -1) * step;
+    l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+  }
+  return t2;
+}
+
+function base3(t, p1, p2, p3, p4) {
+  var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
+    t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
+  return t * t2 - 3 * p1 + 3 * p2;
+}
+
+function cacheKey() {
+  var args = new Array(arguments.length);
+  for (var i = 0; i < args.length; ++i) {
+    args[i] = arguments[i];
+  }
+  i = args.length;
+  var hash = '';
+  while (i--) {
+    hash += (args[i] === Object(args[i])) ?
+      JSON.stringify(args[i]) : args[i];
+  }
+  return hash;
+}
+
+module.exports = p5.Font;
+
+},{"../core/constants":47,"../core/core":48}],83:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule Array Functions
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Adds a value to the end of an array. Extends the length of
+ * the array by one. Maps to Array.push().
+ *
+ * @method append
+ * @param {Array} array Array to append
+ * @param {any} value to be added to the Array
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *
+ * var myArray = new Array("Mango", "Apple", "Papaya")
+ * print(myArray) // ["Mango", "Apple", "Papaya"]
+ *
+ * append(myArray, "Peach")
+ * print(myArray) // ["Mango", "Apple", "Papaya", "Peach"]
+ *
+ * }
+ * </div></code>
+ */
+p5.prototype.append = function(array, value) {
+  array.push(value);
+  return array;
+};
+
+/**
+ * Copies an array (or part of an array) to another array. The src array is
+ * copied to the dst array, beginning at the position specified by
+ * srcPosition and into the position specified by dstPosition. The number of
+ * elements to copy is determined by length. Note that copying values
+ * overwrites existing values in the destination array. To append values
+ * instead of overwriting them, use concat().
+ * <br><br>
+ * The simplified version with only two arguments, arrayCopy(src, dst),
+ * copies an entire array to another of the same size. It is equivalent to
+ * arrayCopy(src, 0, dst, 0, src.length).
+ * <br><br>
+ * Using this function is far more efficient for copying array data than
+ * iterating through a for() loop and copying each element individually.
+ *
+ * @method arrayCopy
+ * @param {Array}  src           the source Array
+ * @param {Number} [srcPosition] starting position in the source Array
+ * @param {Array}  dst           the destination Array
+ * @param {Number} [dstPosition] starting position in the destination Array
+ * @param {Number} [length]      number of Array elements to be copied
+ *
+ * @example
+ *  <div class="norender"><code>
+ *  function setup() {
+ *
+ *    var src = new Array("A", "B", "C");
+ *    var dst = new Array( 1 ,  2 ,  3 );
+ *    var srcPosition = 1;
+ *    var dstPosition = 0;
+ *    var length = 2;
+ *
+ *    print(src); // ["A", "B", "C"]
+ *    print(dst); // [ 1 ,  2 ,  3 ]
+ *
+ *    arrayCopy(src, srcPosition, dst, dstPosition, length);
+ *    print(dst); // ["B", "C", 3]
+ *
+ *    }
+ *  </div></code>
+ */
+p5.prototype.arrayCopy = function(
+  src,
+  srcPosition,
+  dst,
+  dstPosition,
+  length) {
+
+  // the index to begin splicing from dst array
+  var start,
+      end;
+
+  if (typeof length !== 'undefined') {
+
+    end = Math.min(length, src.length);
+    start = dstPosition;
+    src = src.slice(srcPosition, end + srcPosition);
+
+  } else {
+
+    if (typeof dst !== 'undefined') { // src, dst, length
+      // rename  so we don't get confused
+      end = dst;
+      end = Math.min(end, src.length);
+    } else { // src, dst
+      end = src.length;
+    }
+
+    start = 0;
+    // rename  so we don't get confused
+    dst = srcPosition;
+    src = src.slice(0, end);
+  }
+
+  // Since we are not returning the array and JavaScript is pass by reference
+  // we must modify the actual values of the array
+  // instead of reassigning arrays
+  Array.prototype.splice.apply(dst, [start, end].concat(src));
+
+};
+
+/**
+ * Concatenates two arrays, maps to Array.concat(). Does not modify the
+ * input arrays.
+ *
+ * @method concat
+ * @param {Array} a first Array to concatenate
+ * @param {Array} b second Array to concatenate
+ * @return {Array} concatenated array
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var arr1 = new Array("A", "B", "C");
+ *   var arr2 = new Array( 1 ,  2 ,  3 );
+ *
+ *   print(arr1); // ["A","B","C"]
+ *   print(arr2); // [1,2,3]
+ *
+ *   var arr3 = concat(arr1, arr2);
+ *
+ *   print(arr1); // ["A","B","C"]
+ *   print(arr2); // [1,2,3]
+ *   print(arr3); // ["A","B","C",1,2,3]
+ *
+ * }
+ * </div></code>
+ */
+p5.prototype.concat = function(list0, list1) {
+  return list0.concat(list1);
+};
+
+/**
+ * Reverses the order of an array, maps to Array.reverse()
+ *
+ * @method reverse
+ * @param {Array} list Array to reverse
+ * @example
+ * <div class="norender"><code>
+ * function setup() {
+ *   var myArray = new Array("A", "B", "C");
+ *   print(myArray); // ["A","B","C"]
+ *
+ *   reverse(myArray);
+ *   print(myArray); // ["C","B","A"]
+ * }
+ * </div></code>
+ */
+p5.prototype.reverse = function(list) {
+  return list.reverse();
+};
+
+/**
+ * Decreases an array by one element and returns the shortened array,
+ * maps to Array.pop().
+ *
+ * @method shorten
+ * @param  {Array} list Array to shorten
+ * @return {Array} shortened Array
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var myArray = new Array("A", "B", "C");
+ *   print(myArray); // ["A","B","C"]
+ *
+ *   var newArray = shorten(myArray);
+ *   print(myArray); // ["A","B","C"]
+ *   print(newArray); // ["A","B"]
+ * }
+ * </div></code>
+ */
+p5.prototype.shorten = function(list) {
+  list.pop();
+  return list;
+};
+
+/**
+ * Randomizes the order of the elements of an array. Implements
+ * <a href="http://Bost.Ocks.org/mike/shuffle/" target=_blank>
+ * Fisher-Yates Shuffle Algorithm</a>.
+ *
+ * @method shuffle
+ * @param  {Array}   array  Array to shuffle
+ * @param  {Boolean} [bool] modify passed array
+ * @return {Array}   shuffled Array
+ * @example
+ * <div><code>
+ * function setup() {
+ *   var regularArr = ['ABC', 'def', createVector(), TAU, Math.E];
+ *   print(regularArr);
+ *   shuffle(regularArr, true); // force modifications to passed array
+ *   print(regularArr);
+ *
+ *   // By default shuffle() returns a shuffled cloned array:
+ *   var newArr = shuffle(regularArr);
+ *   print(regularArr);
+ *   print(newArr);
+ * }
+ * </code></div>
+ */
+p5.prototype.shuffle = function(arr, bool) {
+  var isView = ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(arr);
+  arr = bool || isView ? arr : arr.slice();
+
+  var rnd, tmp, idx = arr.length;
+  while (idx > 1) {
+    rnd = Math.random()*idx | 0;
+
+    tmp = arr[--idx];
+    arr[idx] = arr[rnd];
+    arr[rnd] = tmp;
+  }
+
+  return arr;
+};
+
+/**
+ * Sorts an array of numbers from smallest to largest, or puts an array of
+ * words in alphabetical order. The original array is not modified; a
+ * re-ordered array is returned. The count parameter states the number of
+ * elements to sort. For example, if there are 12 elements in an array and
+ * count is set to 5, only the first 5 elements in the array will be sorted.
+ *
+ * @method sort
+ * @param {Array} list Array to sort
+ * @param {Number} [count] number of elements to sort, starting from 0
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var words = new Array("banana", "apple", "pear","lime");
+ *   print(words); // ["banana", "apple", "pear", "lime"]
+ *   var count = 4; // length of array
+ *
+ *   sort(words, count);
+ *   print(words); // ["apple", "banana", "lime", "pear"]
+ * }
+ * </div></code>
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var numbers = new Array(2,6,1,5,14,9,8,12);
+ *   print(numbers); // [2,6,1,5,14,9,8,12]
+ *   var count = 5; // Less than the length of the array
+ *
+ *   sort(numbers, count);
+ *   print(numbers); // [1,2,5,6,14,9,8,12]
+ * }
+ * </div></code>
+ */
+p5.prototype.sort = function(list, count) {
+  var arr = count ? list.slice(0, Math.min(count, list.length)) : list;
+  var rest = count ? list.slice(Math.min(count, list.length)) : [];
+  if (typeof arr[0] === 'string') {
+    arr = arr.sort();
+  } else {
+    arr = arr.sort(function(a,b){return a-b;});
+  }
+  return arr.concat(rest);
+};
+
+/**
+ * Inserts a value or an array of values into an existing array. The first
+ * parameter specifies the initial array to be modified, and the second
+ * parameter defines the data to be inserted. The third parameter is an index
+ * value which specifies the array position from which to insert data.
+ * (Remember that array index numbering starts at zero, so the first position
+ * is 0, the second position is 1, and so on.)
+ *
+ * @method splice
+ * @param {Array}  list Array to splice into
+ * @param {any}    value value to be spliced in
+ * @param {Number} position in the array from which to insert data
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var myArray = new Array(0,1,2,3,4);
+ *   var insArray = new Array("A","B","C");
+ *   print(myArray); // [0,1,2,3,4]
+ *   print(insArray); // ["A","B","C"]
+ *
+ *   splice(myArray, insArray, 3);
+ *   print(myArray); // [0,1,2,"A","B","C",3,4]
+ * }
+ * </div></code>
+ */
+p5.prototype.splice = function(list, value, index) {
+
+  // note that splice returns spliced elements and not an array
+  Array.prototype.splice.apply(list, [index, 0].concat(value));
+
+  return list;
+};
+
+/**
+ * Extracts an array of elements from an existing array. The list parameter
+ * defines the array from which the elements will be copied, and the start
+ * and count parameters specify which elements to extract. If no count is
+ * given, elements will be extracted from the start to the end of the array.
+ * When specifying the start, remember that the first array element is 0.
+ * This function does not change the source array.
+ *
+ * @method subset
+ * @param  {Array}  list    Array to extract from
+ * @param  {Number} start   position to begin
+ * @param  {Number} [count] number of values to extract
+ * @return {Array}          Array of extracted elements
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *   var myArray = new Array(1,2,3,4,5);
+ *   print(myArray); // [1,2,3,4,5]
+ *
+ *   var sub1 = subset(myArray, 0, 3);
+ *   var sub2 = subset(myArray, 2, 2);
+ *   print(sub1); // [1,2,3]
+ *   print(sub2); // [3,4]
+ * }
+ * </div></code>
+ */
+p5.prototype.subset = function(list, start, count) {
+  if (typeof count !== 'undefined') {
+    return list.slice(start, start + count);
+  } else {
+    return list.slice(start, list.length);
+  }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],84:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule Conversion
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Converts a string to its floating point representation. The contents of a
+ * string must resemble a number, or NaN (not a number) will be returned.
+ * For example, float("1234.56") evaluates to 1234.56, but float("giraffe")
+ * will return NaN.
+ *
+ * @method float
+ * @param {String}  str float string to parse
+ * @return {Number}     floating point representation of string
+ * @example
+ * <div><code>
+ * var str = '20';
+ * var diameter = float(str);
+ * ellipse(width/2, height/2, diameter, diameter);
+ * </code></div>
+ */
+p5.prototype.float = function(str) {
+  return parseFloat(str);
+};
+
+/**
+ * Converts a boolean, string, or float to its integer representation.
+ * When an array of values is passed in, then an int array of the same length
+ * is returned.
+ *
+ * @method int
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Number}                     integer representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(int("10")); // 10
+ * print(int(10.31)); // 10
+ * print(int(-10)); // -10
+ * print(int(true)); // 1
+ * print(int(false)); // 0
+ * print(int([false, true, "10.3", 9.8])); // [0, 1, 10, 9]
+ * </code></div>
+ */
+p5.prototype.int = function(n, radix) {
+  if (typeof n === 'string') {
+    radix = radix || 10;
+    return parseInt(n, radix);
+  } else if (typeof n === 'number') {
+    return n | 0;
+  } else if (typeof n === 'boolean') {
+    return n ? 1 : 0;
+  } else if (n instanceof Array) {
+    return n.map(function(n) { return p5.prototype.int(n, radix); });
+  }
+};
+
+/**
+ * Converts a boolean, string or number to its string representation.
+ * When an array of values is passed in, then an array of strings of the same
+ * length is returned.
+ *
+ * @method str
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {String}                     string representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(str("10"));  // "10"
+ * print(str(10.31)); // "10.31"
+ * print(str(-10));   // "-10"
+ * print(str(true));  // "true"
+ * print(str(false)); // "false"
+ * print(str([true, "10.3", 9.8])); // [ "true", "10.3", "9.8" ]
+ * </code></div>
+ */
+p5.prototype.str = function(n) {
+  if (n instanceof Array) {
+    return n.map(p5.prototype.str);
+  } else {
+    return String(n);
+  }
+};
+
+/**
+ * Converts a number or string to its boolean representation.
+ * For a number, any non-zero value (positive or negative) evaluates to true,
+ * while zero evaluates to false. For a string, the value "true" evaluates to
+ * true, while any other value evaluates to false. When an array of number or
+ * string values is passed in, then a array of booleans of the same length is
+ * returned.
+ *
+ * @method boolean
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Boolean}                    boolean representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(boolean(0));               // false
+ * print(boolean(1));               // true
+ * print(boolean("true"));          // true
+ * print(boolean("abcd"));          // false
+ * print(boolean([0, 12, "true"])); // [false, true, false]
+ * </code></div>
+ */
+p5.prototype.boolean = function(n) {
+  if (typeof n === 'number') {
+    return n !== 0;
+  } else if (typeof n === 'string') {
+    return n.toLowerCase() === 'true';
+  } else if (typeof n === 'boolean') {
+    return n;
+  } else if (n instanceof Array) {
+    return n.map(p5.prototype.boolean);
+  }
+};
+
+/**
+ * Converts a number, string or boolean to its byte representation.
+ * A byte can be only a whole number between -128 and 127, so when a value
+ * outside of this range is converted, it wraps around to the corresponding
+ * byte representation. When an array of number, string or boolean values is
+ * passed in, then an array of bytes the same length is returned.
+ *
+ * @method byte
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Number}                     byte representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(byte(127));               // 127
+ * print(byte(128));               // -128
+ * print(byte(23.4));              // 23
+ * print(byte("23.4"));            // 23
+ * print(byte(true));              // 1
+ * print(byte([0, 255, "100"]));   // [0, -1, 100]
+ * </code></div>
+ */
+p5.prototype.byte = function(n) {
+  var nn = p5.prototype.int(n, 10);
+  if (typeof nn === 'number') {
+    return ((nn + 128) % 256) - 128;
+  } else if (nn instanceof Array) {
+    return nn.map(p5.prototype.byte);
+  }
+};
+
+/**
+ * Converts a number or string to its corresponding single-character
+ * string representation. If a string parameter is provided, it is first
+ * parsed as an integer and then translated into a single-character string.
+ * When an array of number or string values is passed in, then an array of
+ * single-character strings of the same length is returned.
+ *
+ * @method char
+ * @param {String|Number|Array} n value to parse
+ * @return {String}             string representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(char(65));                     // "A"
+ * print(char("65"));                   // "A"
+ * print(char([65, 66, 67]));           // [ "A", "B", "C" ]
+ * print(join(char([65, 66, 67]), '')); // "ABC"
+ * </code></div>
+ */
+p5.prototype.char = function(n) {
+  if (typeof n === 'number' && !isNaN(n)) {
+    return String.fromCharCode(n);
+  } else if (n instanceof Array) {
+    return n.map(p5.prototype.char);
+  } else if (typeof n === 'string') {
+    return p5.prototype.char(parseInt(n, 10));
+  }
+};
+
+/**
+ * Converts a single-character string to its corresponding integer
+ * representation. When an array of single-character string values is passed
+ * in, then an array of integers of the same length is returned.
+ *
+ * @method unchar
+ * @param {String|Array} n value to parse
+ * @return {Number}      integer representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(unchar("A"));               // 65
+ * print(unchar(["A", "B", "C"]));   // [ 65, 66, 67 ]
+ * print(unchar(split("ABC", "")));  // [ 65, 66, 67 ]
+ * </code></div>
+ */
+p5.prototype.unchar = function(n) {
+  if (typeof n === 'string' && n.length === 1) {
+    return n.charCodeAt(0);
+  } else if (n instanceof Array) {
+    return n.map(p5.prototype.unchar);
+  }
+};
+
+/**
+ * Converts a number to a string in its equivalent hexadecimal notation. If a
+ * second parameter is passed, it is used to set the number of characters to
+ * generate in the hexadecimal notation. When an array is passed in, an
+ * array of strings in hexadecimal notation of the same length is returned.
+ *
+ * @method hex
+ * @param {Number|Array} n value to parse
+ * @return {String}      hexadecimal string representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(hex(255));               // "000000FF"
+ * print(hex(255, 6));            // "0000FF"
+ * print(hex([0, 127, 255], 6));  // [ "000000", "00007F", "0000FF" ]
+ * </code></div>
+ */
+p5.prototype.hex = function(n, digits) {
+  digits = (digits === undefined || digits === null) ? digits = 8 : digits;
+  if (n instanceof Array) {
+    return n.map(function(n) { return p5.prototype.hex(n, digits); });
+  } else if (typeof n === 'number') {
+    if (n < 0) {
+      n = 0xFFFFFFFF + n + 1;
+    }
+    var hex = Number(n).toString(16).toUpperCase();
+    while (hex.length < digits) {
+      hex = '0' + hex;
+    }
+    if (hex.length >= digits) {
+      hex = hex.substring(hex.length - digits, hex.length);
+    }
+    return hex;
+  }
+};
+
+/**
+ * Converts a string representation of a hexadecimal number to its equivalent
+ * integer value. When an array of strings in hexadecimal notation is passed
+ * in, an array of integers of the same length is returned.
+ *
+ * @method unhex
+ * @param {String|Array} n value to parse
+ * @return {Number}      integer representation of hexadecimal value
+ * @example
+ * <div class='norender'><code>
+ * print(unhex("A"));                // 10
+ * print(unhex("FF"));               // 255
+ * print(unhex(["FF", "AA", "00"])); // [ 255, 170, 0 ]
+ * </code></div>
+ */
+p5.prototype.unhex = function(n) {
+  if (n instanceof Array) {
+    return n.map(p5.prototype.unhex);
+  } else {
+    return parseInt('0x' + n, 16);
+  }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],85:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule String Functions
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+//return p5; //LM is this a mistake?
+
+/**
+ * Combines an array of Strings into one String, each separated by the
+ * character(s) used for the separator parameter. To join arrays of ints or
+ * floats, it's necessary to first convert them to Strings using nf() or
+ * nfs().
+ *
+ * @method join
+ * @param  {Array}  list      array of Strings to be joined
+ * @param  {String} separator String to be placed between each item
+ * @return {String}           joined String
+ * @example
+ * <div>
+ * <code>
+ * var array = ["Hello", "world!"]
+ * var separator = " "
+ * var message = join(array, separator);
+ * text(message, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.join = function(list, separator) {
+  return list.join(separator);
+};
+
+/**
+ * This function is used to apply a regular expression to a piece of text,
+ * and return matching groups (elements found inside parentheses) as a
+ * String array. If there are no matches, a null value will be returned.
+ * If no groups are specified in the regular expression, but the sequence
+ * matches, an array of length 1 (with the matched text as the first element
+ * of the array) will be returned.
+ * <br><br>
+ * To use the function, first check to see if the result is null. If the
+ * result is null, then the sequence did not match at all. If the sequence
+ * did match, an array is returned.
+ * <br><br>
+ * If there are groups (specified by sets of parentheses) in the regular
+ * expression, then the contents of each will be returned in the array.
+ * Element [0] of a regular expression match returns the entire matching
+ * string, and the match groups start at element [1] (the first group is [1],
+ * the second [2], and so on).
+ *
+ * @method match
+ * @param  {String} str    the String to be searched
+ * @param  {String} regexp the regexp to be used for matching
+ * @return {Array}         Array of Strings found
+ * @example
+ * <div>
+ * <code>
+ * var string = "Hello p5js*!"
+ * var regexp = "p5js\\*"
+ * var match = match(string, regexp);
+ * text(match, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.match =  function(str, reg) {
+  return str.match(reg);
+};
+
+/**
+ * This function is used to apply a regular expression to a piece of text,
+ * and return a list of matching groups (elements found inside parentheses)
+ * as a two-dimensional String array. If there are no matches, a null value
+ * will be returned. If no groups are specified in the regular expression,
+ * but the sequence matches, a two dimensional array is still returned, but
+ * the second dimension is only of length one.
+ * <br><br>
+ * To use the function, first check to see if the result is null. If the
+ * result is null, then the sequence did not match at all. If the sequence
+ * did match, a 2D array is returned.
+ * <br><br>
+ * If there are groups (specified by sets of parentheses) in the regular
+ * expression, then the contents of each will be returned in the array.
+ * Assuming a loop with counter variable i, element [i][0] of a regular
+ * expression match returns the entire matching string, and the match groups
+ * start at element [i][1] (the first group is [i][1], the second [i][2],
+ * and so on).
+ *
+ * @method matchAll
+ * @param  {String} str    the String to be searched
+ * @param  {String} regexp the regexp to be used for matching
+ * @return {Array}         2d Array of Strings found
+ * @example
+ * <div class="norender">
+ * <code>
+ * var string = "Hello p5js*! Hello world!"
+ * var regexp = "Hello"
+ * matchAll(string, regexp);
+ * </code>
+ * </div>
+
+ */
+p5.prototype.matchAll = function(str, reg) {
+  var re = new RegExp(reg, 'g');
+  var match = re.exec(str);
+  var matches = [];
+  while (match !== null) {
+    matches.push(match);
+    // matched text: match[0]
+    // match start: match.index
+    // capturing group n: match[n]
+    match = re.exec(str);
+  }
+  return matches;
+};
+
+/**
+ * Utility function for formatting numbers into strings. There are two
+ * versions: one for formatting floats, and one for formatting ints.
+ * The values for the digits, left, and right parameters should always
+ * be positive integers.
+ *
+ * @method nf
+ * @param {Number|Array} num      the Number to format
+ * @param {Number}       [left]   number of digits to the left of the
+ *                                decimal point
+ * @param {Number}       [right]  number of digits to the right of the
+ *                                decimal point
+ * @return {String|Array}         formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   background(200);
+ *   var num = 112.53106115;
+ *
+ *   noStroke();
+ *   fill(0);
+ *   textSize(14);
+ *   // Draw formatted numbers
+ *   text(nf(num, 5, 2), 10, 20);
+ *
+ *   text(nf(num, 4, 3), 10, 55);
+ *
+ *   text(nf(num, 3, 6), 10, 85);
+ *
+ *   // Draw dividing lines
+ *   stroke(120);
+ *   line(0, 30, width, 30);
+ *   line(0, 65, width, 65);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nf = function () {
+  if (arguments[0] instanceof Array) {
+    var a = arguments[1];
+    var b = arguments[2];
+    return arguments[0].map(function (x) {
+      return doNf(x, a, b);
+    });
+  }
+  else{
+    var typeOfFirst = Object.prototype.toString.call(arguments[0]);
+    if(typeOfFirst === '[object Arguments]'){
+      if(arguments[0].length===3){
+        return this.nf(arguments[0][0],arguments[0][1],arguments[0][2]);
+      }
+      else if(arguments[0].length===2){
+        return this.nf(arguments[0][0],arguments[0][1]);
+      }
+      else{
+        return this.nf(arguments[0][0]);
+      }
+    }
+    else {
+      return doNf.apply(this, arguments);
+    }
+  }
+};
+
+function doNf() {
+  var num = arguments[0];
+  var neg = num < 0;
+  var n = neg ? num.toString().substring(1) : num.toString();
+  var decimalInd = n.indexOf('.');
+  var intPart = decimalInd !== -1 ? n.substring(0, decimalInd) : n;
+  var decPart = decimalInd !== -1 ? n.substring(decimalInd + 1) : '';
+  var str = neg ? '-' : '';
+  if (arguments.length === 3) {
+    var decimal = '';
+    if(decimalInd !== -1 || arguments[2] - decPart.length > 0){
+      decimal = '.';
+    }
+    if (decPart.length > arguments[2]) {
+      decPart = decPart.substring(0, arguments[2]);
+    }
+    for (var i = 0; i < arguments[1] - intPart.length; i++) {
+      str += '0';
+    }
+    str += intPart;
+    str += decimal;
+    str += decPart;
+    for (var j = 0; j < arguments[2] - decPart.length; j++) {
+      str += '0';
+    }
+    return str;
+  }
+  else {
+    for (var k = 0; k < Math.max(arguments[1] - intPart.length, 0); k++) {
+      str += '0';
+    }
+    str += n;
+    return str;
+  }
+}
+
+/**
+ * Utility function for formatting numbers into strings and placing
+ * appropriate commas to mark units of 1000. There are two versions: one
+ * for formatting ints, and one for formatting an array of ints. The value
+ * for the right parameter should always be a positive integer.
+ *
+ * @method nfc
+ * @param  {Number|Array}   num     the Number to format
+ * @param  {Number}         [right] number of digits to the right of the
+ *                                  decimal point
+ * @return {String|Array}           formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   background(200);
+ *   var num = 11253106.115;
+ *   var numArr = new Array(1,1,2);
+ *
+ *   noStroke();
+ *   fill(0);
+ *   textSize(12);
+ *
+ *   // Draw formatted numbers
+ *   text(nfc(num, 4, 2), 10, 30);
+ *   text(nfc(numArr, 2, 1), 10, 80);
+ *
+ *   // Draw dividing line
+ *   stroke(120);
+ *   line(0, 50, width, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nfc = function () {
+  if (arguments[0] instanceof Array) {
+    var a = arguments[1];
+    return arguments[0].map(function (x) {
+      return doNfc(x, a);
+    });
+  } else {
+    return doNfc.apply(this, arguments);
+  }
+};
+function doNfc() {
+  var num = arguments[0].toString();
+  var dec = num.indexOf('.');
+  var rem = dec !== -1 ? num.substring(dec) : '';
+  var n = dec !== -1 ? num.substring(0, dec) : num;
+  n = n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+  if (arguments[1] === 0) {
+    rem = '';
+  }
+  else if(arguments[1] !== undefined){
+    if(arguments[1] > rem.length){
+      rem+= dec === -1 ? '.' : '';
+      var len = arguments[1] - rem.length + 1;
+      for(var i =0; i< len; i++){
+        rem += '0';
+      }
+    }
+    else{
+      rem = rem.substring(0, arguments[1] + 1);
+    }
+  }
+  return n + rem;
+}
+
+/**
+ * Utility function for formatting numbers into strings. Similar to nf() but
+ * puts a "+" in front of positive numbers and a "-" in front of negative
+ * numbers. There are two versions: one for formatting floats, and one for
+ * formatting ints. The values for left, and right parameters
+ * should always be positive integers.
+ *
+ * @method nfp
+ * @param {Number|Array} num      the Number to format
+ * @param {Number}       [left]   number of digits to the left of the decimal
+ *                                point
+ * @param {Number}       [right]  number of digits to the right of the
+ *                                decimal point
+ * @return {String|Array}         formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   background(200);
+ *   var num1 = 11253106.115;
+ *   var num2 = -11253106.115;
+ *
+ *   noStroke();
+ *   fill(0);
+ *   textSize(12);
+ *
+ *   // Draw formatted numbers
+ *   text(nfp(num1, 4, 2), 10, 30);
+ *   text(nfp(num2, 4, 2), 10, 80);
+ *
+ *   // Draw dividing line
+ *   stroke(120);
+ *   line(0, 50, width, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nfp = function() {
+  var nfRes = this.nf.apply(this, arguments);
+  if (nfRes instanceof Array) {
+    return nfRes.map(addNfp);
+  } else {
+    return addNfp(nfRes);
+  }
+};
+
+function addNfp() {
+  return (
+    parseFloat(arguments[0]) > 0) ?
+    '+'+arguments[0].toString() :
+    arguments[0].toString();
+}
+
+/**
+ * Utility function for formatting numbers into strings. Similar to nf() but
+ * puts a " " (space) in front of positive numbers and a "-" in front of
+ * negative numbers. There are two versions: one for formatting floats, and
+ * one for formatting ints. The values for the digits, left, and right
+ * parameters should always be positive integers.
+ *
+ * @method nfs
+ * @param {Number|Array} num      the Number to format
+ * @param {Number}       [left]   number of digits to the left of the decimal
+ *                                point
+ * @param {Number}       [right]  number of digits to the right of the
+ *                                decimal point
+ * @return {String|Array}         formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ *   background(200);
+ *   var num1 = 11253106.115;
+ *   var num2 = -11253106.115;
+ *
+ *   noStroke();
+ *   fill(0);
+ *   textSize(12);
+ *   // Draw formatted numbers
+ *   text(nfs(num1, 4, 2), 10, 30);
+ *
+ *   text(nfs(num2, 4, 2), 10, 80);
+ *
+ *   // Draw dividing line
+ *   stroke(120);
+ *   line(0, 50, width, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nfs = function() {
+  var nfRes = this.nf.apply(this, arguments);
+  if (nfRes instanceof Array) {
+    return nfRes.map(addNfs);
+  } else {
+    return addNfs(nfRes);
+  }
+};
+
+function addNfs() {
+  return (
+    parseFloat(arguments[0]) > 0) ?
+    ' '+arguments[0].toString() :
+    arguments[0].toString();
+}
+
+/**
+ * The split() function maps to String.split(), it breaks a String into
+ * pieces using a character or string as the delimiter. The delim parameter
+ * specifies the character or characters that mark the boundaries between
+ * each piece. A String[] array is returned that contains each of the pieces.
+ *
+ * The splitTokens() function works in a similar fashion, except that it
+ * splits using a range of characters instead of a specific character or
+ * sequence.
+ *
+ * @method split
+ * @param  {String} value the String to be split
+ * @param  {String} delim the String used to separate the data
+ * @return {Array}  Array of Strings
+ * @example
+ * <div>
+ * <code>
+ * var names = "Pat,Xio,Alex"
+ * var splitString = split(names, ",");
+ * text(splitString[0], 5, 30);
+ * text(splitString[1], 5, 50);
+ * text(splitString[2], 5, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.split = function(str, delim) {
+  return str.split(delim);
+};
+
+/**
+ * The splitTokens() function splits a String at one or many character
+ * delimiters or "tokens." The delim parameter specifies the character or
+ * characters to be used as a boundary.
+ * <br><br>
+ * If no delim characters are specified, any whitespace character is used to
+ * split. Whitespace characters include tab (\t), line feed (\n), carriage
+ * return (\r), form feed (\f), and space.
+ *
+ * @method splitTokens
+ * @param  {String} value   the String to be split
+ * @param  {String} [delim] list of individual Strings that will be used as
+ *                          separators
+ * @return {Array}          Array of Strings
+ * @example
+ * <div class = "norender">
+ * <code>
+ * function setup() {
+ *   var myStr = "Mango, Banana, Lime";
+ *   var myStrArr = splitTokens(myStr, ",");
+ *
+ *   print(myStrArr); // prints : ["Mango"," Banana"," Lime"]
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.splitTokens = function() {
+  var d,sqo,sqc,str;
+  str = arguments[1];
+  if (arguments.length > 1) {
+    sqc = /\]/g.exec(str);
+    sqo = /\[/g.exec(str);
+    if ( sqo && sqc ) {
+      str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
+      sqo = /\[/g.exec(str);
+      str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
+      d = new RegExp('[\\['+str+'\\]]','g');
+    } else if ( sqc ) {
+      str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
+      d = new RegExp('[' + str + '\\]]', 'g');
+    } else if(sqo) {
+      str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
+      d = new RegExp('[' + str + '\\[]', 'g');
+    } else {
+      d = new RegExp('[' + str + ']', 'g');
+    }
+  } else {
+    d = /\s/g;
+  }
+  return arguments[0].split(d).filter(function(n){return n;});
+};
+
+/**
+ * Removes whitespace characters from the beginning and end of a String. In
+ * addition to standard whitespace characters such as space, carriage return,
+ * and tab, this function also removes the Unicode "nbsp" character.
+ *
+ * @method trim
+ * @param  {String|Array} [str] a String or Array of Strings to be trimmed
+ * @return {String|Array}       a trimmed String or Array of Strings
+ * @example
+ * <div>
+ * <code>
+ * var string = trim("  No new lines\n   ");
+ * text(string +" here", 2, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.trim = function(str) {
+  if (str instanceof Array) {
+    return str.map(this.trim);
+  } else {
+    return str.trim();
+  }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],86:[function(_dereq_,module,exports){
+/**
+ * @module Input
+ * @submodule Time & Date
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * p5.js communicates with the clock on your computer. The day() function
+ * returns the current day as a value from 1 - 31.
+ *
+ * @method day
+ * @return {Number} the current day
+ * @example
+ * <div>
+ * <code>
+ * var day = day();
+ * text("Current day: \n" + day, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.day = function() {
+  return new Date().getDate();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The hour() function
+ * returns the current hour as a value from 0 - 23.
+ *
+ * @method hour
+ * @return {Number} the current hour
+ * @example
+ * <div>
+ * <code>
+ * var hour = hour();
+ * text("Current hour:\n" + hour, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.hour = function() {
+  return new Date().getHours();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The minute() function
+ * returns the current minute as a value from 0 - 59.
+ *
+ * @method minute
+ * @return {Number} the current minute
+ * @example
+ * <div>
+ * <code>
+ * var minute = minute();
+ * text("Current minute: \n" + minute, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.minute = function() {
+  return new Date().getMinutes();
+};
+
+/**
+ * Returns the number of milliseconds (thousandths of a second) since
+ * starting the program. This information is often used for timing events and
+ * animation sequences.
+ *
+ * @method millis
+ * @return {Number} the number of milliseconds since starting the program
+ * @example
+ * <div>
+ * <code>
+ * var millisecond = millis();
+ * text("Milliseconds \nrunning: \n" + millisecond, 5, 40);
+ * </code>
+ * </div>
+ */
+p5.prototype.millis = function() {
+  return window.performance.now();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The month() function
+ * returns the current month as a value from 1 - 12.
+ *
+ * @method month
+ * @return {Number} the current month
+ * @example
+ * <div>
+ * <code>
+ * var month = month();
+ * text("Current month: \n" + month, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.month = function() {
+  return new Date().getMonth() + 1; //January is 0!
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The second() function
+ * returns the current second as a value from 0 - 59.
+ *
+ * @method second
+ * @return {Number} the current second
+ * @example
+ * <div>
+ * <code>
+ * var second = second();
+ * text("Current second: \n" + second, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.second = function() {
+  return new Date().getSeconds();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The year() function
+ * returns the current year as an integer (2014, 2015, 2016, etc).
+ *
+ * @method year
+ * @return {Number} the current year
+ * @example
+ * <div>
+ * <code>
+ * var year = year();
+ * text("Current year: \n" + year, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.year = function() {
+  return new Date().getFullYear();
+};
+
+module.exports = p5;
+
+},{"../core/core":48}]},{},[39])(39)
+});
\ No newline at end of file
diff --git a/examples/p5js/libraries/p5.sound.js b/examples/p5js/libraries/p5.sound.js
new file mode 100644
index 0000000000000000000000000000000000000000..65b358a8cbe4a5200a42ef19c58267fc61495432
--- /dev/null
+++ b/examples/p5js/libraries/p5.sound.js
@@ -0,0 +1,9034 @@
+/*! p5.sound.js v0.3.0 2016-01-31 */
+(function (root, factory) {
+  if (typeof define === 'function' && define.amd)
+    define('p5.sound', ['p5'], function (p5) { (factory(p5));});
+  else if (typeof exports === 'object')
+    factory(require('../p5'));
+  else
+    factory(root['p5']);
+}(this, function (p5) {
+  /**
+ *  p5.sound extends p5 with <a href="http://caniuse.com/audio-api"
+ *  target="_blank">Web Audio</a> functionality including audio input,
+ *  playback, analysis and synthesis.
+ *  <br/><br/>
+ *  <a href="#/p5.SoundFile"><b>p5.SoundFile</b></a>: Load and play sound files.<br/>
+ *  <a href="#/p5.Amplitude"><b>p5.Amplitude</b></a>: Get the current volume of a sound.<br/>
+ *  <a href="#/p5.AudioIn"><b>p5.AudioIn</b></a>: Get sound from an input source, typically
+ *    a computer microphone.<br/>
+ *  <a href="#/p5.FFT"><b>p5.FFT</b></a>: Analyze the frequency of sound. Returns
+ *    results from the frequency spectrum or time domain (waveform).<br/>
+ *  <a href="#/p5.Oscillator"><b>p5.Oscillator</b></a>: Generate Sine,
+ *    Triangle, Square and Sawtooth waveforms. Base class of
+ *    <a href="#/p5.Noise">p5.Noise</a> and <a href="#/p5.Pulse">p5.Pulse</a>.
+ *    <br/>
+ *  <a href="#/p5.Env"><b>p5.Env</b></a>: An Envelope is a series
+ *    of fades over time. Often used to control an object's
+ *    output gain level as an "ADSR Envelope" (Attack, Decay,
+ *    Sustain, Release). Can also modulate other parameters.<br/>
+ *  <a href="#/p5.Delay"><b>p5.Delay</b></a>: A delay effect with
+ *    parameters for feedback, delayTime, and lowpass filter.<br/>
+ *  <a href="#/p5.Filter"><b>p5.Filter</b></a>: Filter the frequency range of a
+ *    sound.
+ *  <br/>
+ *  <a href="#/p5.Reverb"><b>p5.Reverb</b></a>: Add reverb to a sound by specifying
+ *    duration and decay. <br/>
+ *  <b><a href="#/p5.Convolver">p5.Convolver</a>:</b> Extends
+ *  <a href="#/p5.Reverb">p5.Reverb</a> to simulate the sound of real
+ *    physical spaces through convolution.<br/>
+ *  <b><a href="#/p5.SoundRecorder">p5.SoundRecorder</a></b>: Record sound for playback 
+ *    / save the .wav file.
+ *  <b><a href="#/p5.Phrase">p5.Phrase</a></b>, <b><a href="#/p5.Part">p5.Part</a></b> and
+ *  <b><a href="#/p5.Score">p5.Score</a></b>: Compose musical sequences.
+ *  <br/><br/>
+ *  p5.sound is on <a href="https://github.com/therewasaguy/p5.sound/">GitHub</a>.
+ *  Download the latest version 
+ *  <a href="https://github.com/therewasaguy/p5.sound/blob/master/lib/p5.sound.js">here</a>.
+ *  
+ *  @module p5.sound
+ *  @submodule p5.sound
+ *  @for p5.sound
+ *  @main
+ */
+/**
+ *  p5.sound developed by Jason Sigal for the Processing Foundation, Google Summer of Code 2014. The MIT License (MIT).
+ *  
+ *  http://github.com/therewasaguy/p5.sound
+ *
+ *  Some of the many audio libraries & resources that inspire p5.sound:
+ *   - TONE.js (c) Yotam Mann, 2014. Licensed under The MIT License (MIT). https://github.com/TONEnoTONE/Tone.js
+ *   - buzz.js (c) Jay Salvat, 2013. Licensed under The MIT License (MIT). http://buzz.jaysalvat.com/
+ *   - Boris Smus Web Audio API book, 2013. Licensed under the Apache License http://www.apache.org/licenses/LICENSE-2.0
+ *   - wavesurfer.js https://github.com/katspaugh/wavesurfer.js
+ *   - Web Audio Components by Jordan Santell https://github.com/web-audio-components
+ *   - Wilm Thoben's Sound library for Processing https://github.com/processing/processing/tree/master/java/libraries/sound
+ *   
+ *   Web Audio API: http://w3.org/TR/webaudio/
+ */
+var sndcore;
+sndcore = function () {
+  'use strict';
+  /* AudioContext Monkeypatch
+     Copyright 2013 Chris Wilson
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+         http://www.apache.org/licenses/LICENSE-2.0
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+  */
+  (function (global, exports, perf) {
+    exports = exports || {};
+    'use strict';
+    function fixSetTarget(param) {
+      if (!param)
+        // if NYI, just return
+        return;
+      if (!param.setTargetAtTime)
+        param.setTargetAtTime = param.setTargetValueAtTime;
+    }
+    if (window.hasOwnProperty('webkitAudioContext') && !window.hasOwnProperty('AudioContext')) {
+      window.AudioContext = webkitAudioContext;
+      if (!AudioContext.prototype.hasOwnProperty('createGain'))
+        AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
+      if (!AudioContext.prototype.hasOwnProperty('createDelay'))
+        AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
+      if (!AudioContext.prototype.hasOwnProperty('createScriptProcessor'))
+        AudioContext.prototype.createScriptProcessor = AudioContext.prototype.createJavaScriptNode;
+      if (!AudioContext.prototype.hasOwnProperty('createPeriodicWave'))
+        AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
+      AudioContext.prototype.internal_createGain = AudioContext.prototype.createGain;
+      AudioContext.prototype.createGain = function () {
+        var node = this.internal_createGain();
+        fixSetTarget(node.gain);
+        return node;
+      };
+      AudioContext.prototype.internal_createDelay = AudioContext.prototype.createDelay;
+      AudioContext.prototype.createDelay = function (maxDelayTime) {
+        var node = maxDelayTime ? this.internal_createDelay(maxDelayTime) : this.internal_createDelay();
+        fixSetTarget(node.delayTime);
+        return node;
+      };
+      AudioContext.prototype.internal_createBufferSource = AudioContext.prototype.createBufferSource;
+      AudioContext.prototype.createBufferSource = function () {
+        var node = this.internal_createBufferSource();
+        if (!node.start) {
+          node.start = function (when, offset, duration) {
+            if (offset || duration)
+              this.noteGrainOn(when || 0, offset, duration);
+            else
+              this.noteOn(when || 0);
+          };
+        } else {
+          node.internal_start = node.start;
+          node.start = function (when, offset, duration) {
+            if (typeof duration !== 'undefined')
+              node.internal_start(when || 0, offset, duration);
+            else
+              node.internal_start(when || 0, offset || 0);
+          };
+        }
+        if (!node.stop) {
+          node.stop = function (when) {
+            this.noteOff(when || 0);
+          };
+        } else {
+          node.internal_stop = node.stop;
+          node.stop = function (when) {
+            node.internal_stop(when || 0);
+          };
+        }
+        fixSetTarget(node.playbackRate);
+        return node;
+      };
+      AudioContext.prototype.internal_createDynamicsCompressor = AudioContext.prototype.createDynamicsCompressor;
+      AudioContext.prototype.createDynamicsCompressor = function () {
+        var node = this.internal_createDynamicsCompressor();
+        fixSetTarget(node.threshold);
+        fixSetTarget(node.knee);
+        fixSetTarget(node.ratio);
+        fixSetTarget(node.reduction);
+        fixSetTarget(node.attack);
+        fixSetTarget(node.release);
+        return node;
+      };
+      AudioContext.prototype.internal_createBiquadFilter = AudioContext.prototype.createBiquadFilter;
+      AudioContext.prototype.createBiquadFilter = function () {
+        var node = this.internal_createBiquadFilter();
+        fixSetTarget(node.frequency);
+        fixSetTarget(node.detune);
+        fixSetTarget(node.Q);
+        fixSetTarget(node.gain);
+        return node;
+      };
+      if (AudioContext.prototype.hasOwnProperty('createOscillator')) {
+        AudioContext.prototype.internal_createOscillator = AudioContext.prototype.createOscillator;
+        AudioContext.prototype.createOscillator = function () {
+          var node = this.internal_createOscillator();
+          if (!node.start) {
+            node.start = function (when) {
+              this.noteOn(when || 0);
+            };
+          } else {
+            node.internal_start = node.start;
+            node.start = function (when) {
+              node.internal_start(when || 0);
+            };
+          }
+          if (!node.stop) {
+            node.stop = function (when) {
+              this.noteOff(when || 0);
+            };
+          } else {
+            node.internal_stop = node.stop;
+            node.stop = function (when) {
+              node.internal_stop(when || 0);
+            };
+          }
+          if (!node.setPeriodicWave)
+            node.setPeriodicWave = node.setWaveTable;
+          fixSetTarget(node.frequency);
+          fixSetTarget(node.detune);
+          return node;
+        };
+      }
+    }
+    if (window.hasOwnProperty('webkitOfflineAudioContext') && !window.hasOwnProperty('OfflineAudioContext')) {
+      window.OfflineAudioContext = webkitOfflineAudioContext;
+    }
+    return exports;
+  }(window));
+  // <-- end MonkeyPatch.
+  // Create the Audio Context
+  var audiocontext = new window.AudioContext();
+  /**
+   * <p>Returns the Audio Context for this sketch. Useful for users
+   * who would like to dig deeper into the <a target='_blank' href=
+   * 'http://webaudio.github.io/web-audio-api/'>Web Audio API
+   * </a>.</p>
+   *
+   * @method getAudioContext
+   * @return {Object}    AudioContext for this sketch
+   */
+  p5.prototype.getAudioContext = function () {
+    return audiocontext;
+  };
+  // Polyfill for AudioIn, also handled by p5.dom createCapture
+  navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+  /**
+   * Determine which filetypes are supported (inspired by buzz.js)
+   * The audio element (el) will only be used to test browser support for various audio formats
+   */
+  var el = document.createElement('audio');
+  p5.prototype.isSupported = function () {
+    return !!el.canPlayType;
+  };
+  var isOGGSupported = function () {
+    return !!el.canPlayType && el.canPlayType('audio/ogg; codecs="vorbis"');
+  };
+  var isMP3Supported = function () {
+    return !!el.canPlayType && el.canPlayType('audio/mpeg;');
+  };
+  var isWAVSupported = function () {
+    return !!el.canPlayType && el.canPlayType('audio/wav; codecs="1"');
+  };
+  var isAACSupported = function () {
+    return !!el.canPlayType && (el.canPlayType('audio/x-m4a;') || el.canPlayType('audio/aac;'));
+  };
+  var isAIFSupported = function () {
+    return !!el.canPlayType && el.canPlayType('audio/x-aiff;');
+  };
+  p5.prototype.isFileSupported = function (extension) {
+    switch (extension.toLowerCase()) {
+    case 'mp3':
+      return isMP3Supported();
+    case 'wav':
+      return isWAVSupported();
+    case 'ogg':
+      return isOGGSupported();
+    case 'aac', 'm4a', 'mp4':
+      return isAACSupported();
+    case 'aif', 'aiff':
+      return isAIFSupported();
+    default:
+      return false;
+    }
+  };
+  // if it is iOS, we have to have a user interaction to start Web Audio
+  // http://paulbakaus.com/tutorials/html5/web-audio-on-ios/
+  var iOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false;
+  if (iOS) {
+    var iosStarted = false;
+    var startIOS = function () {
+      if (iosStarted)
+        return;
+      // create empty buffer
+      var buffer = audiocontext.createBuffer(1, 1, 22050);
+      var source = audiocontext.createBufferSource();
+      source.buffer = buffer;
+      // connect to output (your speakers)
+      source.connect(audiocontext.destination);
+      // play the file
+      source.start(0);
+      console.log('start ios!');
+      if (audiocontext.state === 'running') {
+        iosStarted = true;
+      }
+    };
+    document.addEventListener('touchend', startIOS, false);
+    document.addEventListener('touchstart', startIOS, false);
+  }
+}();
+var master;
+master = function () {
+  'use strict';
+  /**
+   * Master contains AudioContext and the master sound output.
+   */
+  var Master = function () {
+    var audiocontext = p5.prototype.getAudioContext();
+    this.input = audiocontext.createGain();
+    this.output = audiocontext.createGain();
+    //put a hard limiter on the output
+    this.limiter = audiocontext.createDynamicsCompressor();
+    this.limiter.threshold.value = 0;
+    this.limiter.ratio.value = 100;
+    this.audiocontext = audiocontext;
+    this.output.disconnect();
+    // an array of input sources
+    this.inputSources = [];
+    // connect input to limiter
+    this.input.connect(this.limiter);
+    // connect limiter to output
+    this.limiter.connect(this.output);
+    // meter is just for global Amplitude / FFT analysis
+    this.meter = audiocontext.createGain();
+    this.fftMeter = audiocontext.createGain();
+    this.output.connect(this.meter);
+    this.output.connect(this.fftMeter);
+    // connect output to destination
+    this.output.connect(this.audiocontext.destination);
+    // an array of all sounds in the sketch
+    this.soundArray = [];
+    // an array of all musical parts in the sketch
+    this.parts = [];
+    // file extensions to search for
+    this.extensions = [];
+  };
+  // create a single instance of the p5Sound / master output for use within this sketch
+  var p5sound = new Master();
+  /**
+   * Returns a number representing the master amplitude (volume) for sound 
+   * in this sketch.
+   * 
+   * @method getMasterVolume
+   * @return {Number} Master amplitude (volume) for sound in this sketch.
+   *                  Should be between 0.0 (silence) and 1.0.
+   */
+  p5.prototype.getMasterVolume = function () {
+    return p5sound.output.gain.value;
+  };
+  /**
+   *  <p>Scale the output of all sound in this sketch</p>
+   *  Scaled between 0.0 (silence) and 1.0 (full volume).
+   *  1.0 is the maximum amplitude of a digital sound, so multiplying
+   *  by greater than 1.0 may cause digital distortion. To
+   *  fade, provide a <code>rampTime</code> parameter. For more
+   *  complex fades, see the Env class.
+   *
+   *  Alternately, you can pass in a signal source such as an
+   *  oscillator to modulate the amplitude with an audio signal.
+   *
+   *  <p><b>How This Works</b>: When you load the p5.sound module, it
+   *  creates a single instance of p5sound. All sound objects in this
+   *  module output to p5sound before reaching your computer's output.
+   *  So if you change the amplitude of p5sound, it impacts all of the
+   *  sound in this module.</p>
+   *
+   *  <p>If no value is provided, returns a Web Audio API Gain Node</p>
+   *
+   *  @method  masterVolume
+   *  @param {Number|Object} volume  Volume (amplitude) between 0.0
+   *                                     and 1.0 or modulating signal/oscillator
+   *  @param {Number} [rampTime]  Fade for t seconds
+   *  @param {Number} [timeFromNow]  Schedule this event to happen at
+   *                                 t seconds in the future
+   */
+  p5.prototype.masterVolume = function (vol, rampTime, tFromNow) {
+    if (typeof vol === 'number') {
+      var rampTime = rampTime || 0;
+      var tFromNow = tFromNow || 0;
+      var now = p5sound.audiocontext.currentTime;
+      var currentVol = p5sound.output.gain.value;
+      p5sound.output.gain.cancelScheduledValues(now + tFromNow);
+      p5sound.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
+      p5sound.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
+    } else if (vol) {
+      vol.connect(p5sound.output.gain);
+    } else {
+      // return the Gain Node
+      return p5sound.output.gain;
+    }
+  };
+  /**
+   *  p5.soundOut is the p5.sound master output. It sends output to
+   *  the destination of this window's web audio context. It contains 
+   *  Web Audio API nodes including a dyanmicsCompressor (<code>.limiter</code>),
+   *  and Gain Nodes for <code>.input</code> and <code>.output</code>.
+   *  
+   *  @property p5.soundOut
+   *  @type {Object}
+   */
+  p5.soundOut = p5sound;
+  /**
+   *  a silent connection to the DesinationNode
+   *  which will ensure that anything connected to it
+   *  will not be garbage collected
+   *  
+   *  @private
+   */
+  p5.soundOut._silentNode = p5sound.audiocontext.createGain();
+  p5.soundOut._silentNode.gain.value = 0;
+  p5.soundOut._silentNode.connect(p5sound.audiocontext.destination);
+  return p5sound;
+}(sndcore);
+var helpers;
+helpers = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+   * Returns a number representing the sample rate, in samples per second,
+   * of all sound objects in this audio context. It is determined by the
+   * sampling rate of your operating system's sound card, and it is not
+   * currently possile to change.
+   * It is often 44100, or twice the range of human hearing.
+   *
+   * @method sampleRate
+   * @return {Number} samplerate samples per second
+   */
+  p5.prototype.sampleRate = function () {
+    return p5sound.audiocontext.sampleRate;
+  };
+  /**
+   *  Returns the closest MIDI note value for
+   *  a given frequency.
+   *  
+   *  @param  {Number} frequency A freqeuncy, for example, the "A"
+   *                             above Middle C is 440Hz
+   *  @return {Number}   MIDI note value
+   */
+  p5.prototype.freqToMidi = function (f) {
+    var mathlog2 = Math.log(f / 440) / Math.log(2);
+    var m = Math.round(12 * mathlog2) + 57;
+    return m;
+  };
+  /**
+   *  Returns the frequency value of a MIDI note value.
+   *  General MIDI treats notes as integers where middle C
+   *  is 60, C# is 61, D is 62 etc. Useful for generating
+   *  musical frequencies with oscillators.
+   *  
+   *  @method  midiToFreq
+   *  @param  {Number} midiNote The number of a MIDI note
+   *  @return {Number} Frequency value of the given MIDI note
+   *  @example
+   *  <div><code>
+   *  var notes = [60, 64, 67, 72];
+   *  var i = 0;
+   *  
+   *  function setup() {
+   *    osc = new p5.Oscillator('Triangle');
+   *    osc.start();
+   *    frameRate(1);
+   *  }
+   *  
+   *  function draw() {
+   *    var freq = midiToFreq(notes[i]);
+   *    osc.freq(freq);
+   *    i++;
+   *    if (i >= notes.length){
+   *      i = 0;
+   *    }
+   *  }
+   *  </code></div>
+   */
+  p5.prototype.midiToFreq = function (m) {
+    return 440 * Math.pow(2, (m - 69) / 12);
+  };
+  /**
+   *  List the SoundFile formats that you will include. LoadSound 
+   *  will search your directory for these extensions, and will pick
+   *  a format that is compatable with the client's web browser.
+   *  <a href="http://media.io/">Here</a> is a free online file
+   *  converter.
+   *  
+   *  @method soundFormats
+   *  @param {String|Strings} formats i.e. 'mp3', 'wav', 'ogg'
+   *  @example
+   *  <div><code>
+   *  function preload() {
+   *    // set the global sound formats
+   *    soundFormats('mp3', 'ogg');
+   *    
+   *    // load either beatbox.mp3, or .ogg, depending on browser
+   *    mySound = loadSound('../sounds/beatbox.mp3');
+   *  }
+   *
+   *  function setup() {
+   *    mySound.play();
+   *  }
+   *  </code></div>
+   */
+  p5.prototype.soundFormats = function () {
+    // reset extensions array
+    p5sound.extensions = [];
+    // add extensions
+    for (var i = 0; i < arguments.length; i++) {
+      arguments[i] = arguments[i].toLowerCase();
+      if ([
+          'mp3',
+          'wav',
+          'ogg',
+          'm4a',
+          'aac'
+        ].indexOf(arguments[i]) > -1) {
+        p5sound.extensions.push(arguments[i]);
+      } else {
+        throw arguments[i] + ' is not a valid sound format!';
+      }
+    }
+  };
+  p5.prototype.disposeSound = function () {
+    for (var i = 0; i < p5sound.soundArray.length; i++) {
+      p5sound.soundArray[i].dispose();
+    }
+  };
+  // register removeSound to dispose of p5sound SoundFiles, Convolvers,
+  // Oscillators etc when sketch ends
+  p5.prototype.registerMethod('remove', p5.prototype.disposeSound);
+  p5.prototype._checkFileFormats = function (paths) {
+    var path;
+    // if path is a single string, check to see if extension is provided
+    if (typeof paths === 'string') {
+      path = paths;
+      // see if extension is provided
+      var extTest = path.split('.').pop();
+      // if an extension is provided...
+      if ([
+          'mp3',
+          'wav',
+          'ogg',
+          'm4a',
+          'aac'
+        ].indexOf(extTest) > -1) {
+        var supported = p5.prototype.isFileSupported(extTest);
+        if (supported) {
+          path = path;
+        } else {
+          var pathSplit = path.split('.');
+          var pathCore = pathSplit[pathSplit.length - 1];
+          for (var i = 0; i < p5sound.extensions.length; i++) {
+            var extension = p5sound.extensions[i];
+            var supported = p5.prototype.isFileSupported(extension);
+            if (supported) {
+              pathCore = '';
+              if (pathSplit.length === 2) {
+                pathCore += pathSplit[0];
+              }
+              for (var i = 1; i <= pathSplit.length - 2; i++) {
+                var p = pathSplit[i];
+                pathCore += '.' + p;
+              }
+              path = pathCore += '.';
+              path = path += extension;
+              break;
+            }
+          }
+        }
+      } else {
+        for (var i = 0; i < p5sound.extensions.length; i++) {
+          var extension = p5sound.extensions[i];
+          var supported = p5.prototype.isFileSupported(extension);
+          if (supported) {
+            path = path + '.' + extension;
+            break;
+          }
+        }
+      }
+    } else if (typeof paths === 'object') {
+      for (var i = 0; i < paths.length; i++) {
+        var extension = paths[i].split('.').pop();
+        var supported = p5.prototype.isFileSupported(extension);
+        if (supported) {
+          // console.log('.'+extension + ' is ' + supported +
+          //  ' supported by your browser.');
+          path = paths[i];
+          break;
+        }
+      }
+    }
+    return path;
+  };
+  /**
+   *  Used by Osc and Env to chain signal math
+   */
+  p5.prototype._mathChain = function (o, math, thisChain, nextChain, type) {
+    // if this type of math already exists in the chain, replace it
+    for (var i in o.mathOps) {
+      if (o.mathOps[i] instanceof type) {
+        o.mathOps[i].dispose();
+        thisChain = i;
+        if (thisChain < o.mathOps.length - 1) {
+          nextChain = o.mathOps[i + 1];
+        }
+      }
+    }
+    o.mathOps[thisChain - 1].disconnect();
+    o.mathOps[thisChain - 1].connect(math);
+    math.connect(nextChain);
+    o.mathOps[thisChain] = math;
+    return o;
+  };
+}(master);
+var errorHandler;
+errorHandler = function () {
+  'use strict';
+  /**
+   *  Helper function to generate an error
+   *  with a custom stack trace that points to the sketch
+   *  and removes other parts of the stack trace.
+   *  
+   *  @private
+   *  
+   *  @param  {String} name         custom  error name
+   *  @param  {String} errorTrace   custom error trace
+   *  @param  {String} failedPath     path to the file that failed to load
+   *  @property {String} name custom error name
+   *  @property {String} message custom error message
+   *  @property {String} stack trace the error back to a line in the user's sketch.
+   *                           Note: this edits out stack trace within p5.js and p5.sound.
+   *  @property {String} originalStack unedited, original stack trace
+   *  @property {String} failedPath path to the file that failed to load
+   *  @return {Error}     returns a custom Error object
+   */
+  var CustomError = function (name, errorTrace, failedPath) {
+    var err = new Error();
+    var tempStack, splitStack;
+    err.name = name;
+    err.originalStack = err.stack + errorTrace;
+    tempStack = err.stack + errorTrace;
+    err.failedPath = failedPath;
+    // only print the part of the stack trace that refers to the user code:
+    var splitStack = tempStack.split('\n');
+    splitStack = splitStack.filter(function (ln) {
+      return !ln.match(/(p5.|native code|globalInit)/g);
+    });
+    err.stack = splitStack.join('\n');
+    return err;
+  };
+  return CustomError;
+}();
+var panner;
+panner = function () {
+  'use strict';
+  var p5sound = master;
+  var ac = p5sound.audiocontext;
+  // Stereo panner
+  // if there is a stereo panner node use it
+  if (typeof ac.createStereoPanner !== 'undefined') {
+    p5.Panner = function (input, output, numInputChannels) {
+      this.stereoPanner = this.input = ac.createStereoPanner();
+      input.connect(this.stereoPanner);
+      this.stereoPanner.connect(output);
+    };
+    p5.Panner.prototype.pan = function (val, tFromNow) {
+      var time = tFromNow || 0;
+      var t = ac.currentTime + time;
+      this.stereoPanner.pan.linearRampToValueAtTime(val, t);
+    };
+    p5.Panner.prototype.inputChannels = function (numChannels) {
+    };
+    p5.Panner.prototype.connect = function (obj) {
+      this.stereoPanner.connect(obj);
+    };
+    p5.Panner.prototype.disconnect = function (obj) {
+      this.stereoPanner.disconnect();
+    };
+  } else {
+    // if there is no createStereoPanner object
+    // such as in safari 7.1.7 at the time of writing this
+    // use this method to create the effect
+    p5.Panner = function (input, output, numInputChannels) {
+      this.input = ac.createGain();
+      input.connect(this.input);
+      this.left = ac.createGain();
+      this.right = ac.createGain();
+      this.left.channelInterpretation = 'discrete';
+      this.right.channelInterpretation = 'discrete';
+      // if input is stereo
+      if (numInputChannels > 1) {
+        this.splitter = ac.createChannelSplitter(2);
+        this.input.connect(this.splitter);
+        this.splitter.connect(this.left, 1);
+        this.splitter.connect(this.right, 0);
+      } else {
+        this.input.connect(this.left);
+        this.input.connect(this.right);
+      }
+      this.output = ac.createChannelMerger(2);
+      this.left.connect(this.output, 0, 1);
+      this.right.connect(this.output, 0, 0);
+      this.output.connect(output);
+    };
+    // -1 is left, +1 is right
+    p5.Panner.prototype.pan = function (val, tFromNow) {
+      var time = tFromNow || 0;
+      var t = ac.currentTime + time;
+      var v = (val + 1) / 2;
+      var rightVal = Math.cos(v * Math.PI / 2);
+      var leftVal = Math.sin(v * Math.PI / 2);
+      this.left.gain.linearRampToValueAtTime(leftVal, t);
+      this.right.gain.linearRampToValueAtTime(rightVal, t);
+    };
+    p5.Panner.prototype.inputChannels = function (numChannels) {
+      if (numChannels === 1) {
+        this.input.disconnect();
+        this.input.connect(this.left);
+        this.input.connect(this.right);
+      } else if (numChannels === 2) {
+        if (typeof (this.splitter === 'undefined')) {
+          this.splitter = ac.createChannelSplitter(2);
+        }
+        this.input.disconnect();
+        this.input.connect(this.splitter);
+        this.splitter.connect(this.left, 1);
+        this.splitter.connect(this.right, 0);
+      }
+    };
+    p5.Panner.prototype.connect = function (obj) {
+      this.output.connect(obj);
+    };
+    p5.Panner.prototype.disconnect = function (obj) {
+      this.output.disconnect();
+    };
+  }
+  // 3D panner
+  p5.Panner3D = function (input, output) {
+    var panner3D = ac.createPanner();
+    panner3D.panningModel = 'HRTF';
+    panner3D.distanceModel = 'linear';
+    panner3D.setPosition(0, 0, 0);
+    input.connect(panner3D);
+    panner3D.connect(output);
+    panner3D.pan = function (xVal, yVal, zVal) {
+      panner3D.setPosition(xVal, yVal, zVal);
+    };
+    return panner3D;
+  };
+}(master);
+var soundfile;
+soundfile = function () {
+  'use strict';
+  var CustomError = errorHandler;
+  var p5sound = master;
+  var ac = p5sound.audiocontext;
+  /**
+   *  <p>SoundFile object with a path to a file.</p>
+   *  
+   *  <p>The p5.SoundFile may not be available immediately because
+   *  it loads the file information asynchronously.</p>
+   * 
+   *  <p>To do something with the sound as soon as it loads
+   *  pass the name of a function as the second parameter.</p>
+   *  
+   *  <p>Only one file path is required. However, audio file formats 
+   *  (i.e. mp3, ogg, wav and m4a/aac) are not supported by all
+   *  web browsers. If you want to ensure compatability, instead of a single
+   *  file path, you may include an Array of filepaths, and the browser will
+   *  choose a format that works.</p>
+   * 
+   *  @class p5.SoundFile
+   *  @constructor
+   *  @param {String/Array} path   path to a sound file (String). Optionally,
+   *                               you may include multiple file formats in
+   *                               an array. Alternately, accepts an object
+   *                               from the HTML5 File API, or a p5.File.
+   *  @param {Function} [successCallback]   Name of a function to call once file loads
+   *  @param {Function} [errorCallback]   Name of a function to call if file fails to
+   *                                      load. This function will receive an error or
+   *                                     XMLHttpRequest object with information
+   *                                     about what went wrong.
+   *  @param {Function} [whileLoadingCallback]   Name of a function to call while file
+   *                                             is loading. That function will
+   *                                             receive percentage loaded
+   *                                             (between 0 and 1) as a
+   *                                             parameter.
+   *                                             
+   *  @return {Object}    p5.SoundFile Object
+   *  @example 
+   *  <div><code>
+   *  
+   *  function preload() {
+   *    mySound = loadSound('assets/doorbell.mp3');
+   *  }
+   *
+   *  function setup() {
+   *    mySound.setVolume(0.1);
+   *    mySound.play();
+   *  }
+   * 
+   * </code></div>
+   */
+  p5.SoundFile = function (paths, onload, onerror, whileLoading) {
+    if (typeof paths !== 'undefined') {
+      if (typeof paths == 'string' || typeof paths[0] == 'string') {
+        var path = p5.prototype._checkFileFormats(paths);
+        this.url = path;
+      } else if (typeof paths == 'object') {
+        if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
+          // The File API isn't supported in this browser 
+          throw 'Unable to load file because the File API is not supported';
+        }
+      }
+      // if type is a p5.File...get the actual file
+      if (paths.file) {
+        paths = paths.file;
+      }
+      this.file = paths;
+    }
+    // private _onended callback, set by the method: onended(callback)
+    this._onended = function () {
+    };
+    this._looping = false;
+    this._playing = false;
+    this._paused = false;
+    this._pauseTime = 0;
+    // cues for scheduling events with addCue() removeCue()
+    this._cues = [];
+    //  position of the most recently played sample
+    this._lastPos = 0;
+    this._counterNode;
+    this._scopeNode;
+    // array of sources so that they can all be stopped!
+    this.bufferSourceNodes = [];
+    // current source
+    this.bufferSourceNode = null;
+    this.buffer = null;
+    this.playbackRate = 1;
+    this.gain = 1;
+    this.input = p5sound.audiocontext.createGain();
+    this.output = p5sound.audiocontext.createGain();
+    this.reversed = false;
+    // start and end of playback / loop
+    this.startTime = 0;
+    this.endTime = null;
+    this.pauseTime = 0;
+    // "restart" would stop playback before retriggering
+    this.mode = 'sustain';
+    // time that playback was started, in millis
+    this.startMillis = null;
+    // stereo panning
+    this.panPosition = 0;
+    this.panner = new p5.Panner(this.output, p5sound.input, 2);
+    // it is possible to instantiate a soundfile with no path
+    if (this.url || this.file) {
+      this.load(onload, onerror);
+    }
+    // add this p5.SoundFile to the soundArray
+    p5sound.soundArray.push(this);
+    if (typeof whileLoading === 'function') {
+      this._whileLoading = whileLoading;
+    } else {
+      this._whileLoading = function () {
+      };
+    }
+  };
+  // register preload handling of loadSound
+  p5.prototype.registerPreloadMethod('loadSound', p5.prototype);
+  /**
+   *  loadSound() returns a new p5.SoundFile from a specified
+   *  path. If called during preload(), the p5.SoundFile will be ready
+   *  to play in time for setup() and draw(). If called outside of
+   *  preload, the p5.SoundFile will not be ready immediately, so
+   *  loadSound accepts a callback as the second parameter. Using a
+   *  <a href="https://github.com/processing/p5.js/wiki/Local-server">
+   *  local server</a> is recommended when loading external files.
+   *  
+   *  @method loadSound
+   *  @param  {String/Array}   path     Path to the sound file, or an array with
+   *                                    paths to soundfiles in multiple formats
+   *                                    i.e. ['sound.ogg', 'sound.mp3'].
+   *                                    Alternately, accepts an object: either
+   *                                    from the HTML5 File API, or a p5.File.
+   *  @param {Function} [successCallback]   Name of a function to call once file loads
+   *  @param {Function} [errorCallback]   Name of a function to call if there is
+   *                                      an error loading the file.
+   *  @param {Function} [whileLoading] Name of a function to call while file is loading.
+   *                                 This function will receive the percentage loaded
+   *                                 so far, from 0.0 to 1.0.
+   *  @return {SoundFile}            Returns a p5.SoundFile
+   *  @example 
+   *  <div><code>
+   *  function preload() {
+   *   mySound = loadSound('assets/doorbell.mp3');
+   *  }
+   *
+   *  function setup() {
+   *    mySound.setVolume(0.1);
+   *    mySound.play();
+   *  }
+   *  </code></div>
+   */
+  p5.prototype.loadSound = function (path, callback, onerror, whileLoading) {
+    // if loading locally without a server
+    if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
+      alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
+    }
+    var s = new p5.SoundFile(path, callback, onerror, whileLoading);
+    return s;
+  };
+  /**
+   * This is a helper function that the p5.SoundFile calls to load
+   * itself. Accepts a callback (the name of another function)
+   * as an optional parameter.
+   *
+   * @private
+   * @param {Function} [successCallback]   Name of a function to call once file loads
+   * @param {Function} [errorCallback]   Name of a function to call if there is an error
+   */
+  p5.SoundFile.prototype.load = function (callback, errorCallback) {
+    var loggedError = false;
+    var self = this;
+    var errorTrace = new Error().stack;
+    if (this.url != undefined && this.url != '') {
+      var request = new XMLHttpRequest();
+      request.addEventListener('progress', function (evt) {
+        self._updateProgress(evt);
+      }, false);
+      request.open('GET', this.url, true);
+      request.responseType = 'arraybuffer';
+      request.onload = function () {
+        if (request.status == 200) {
+          // on sucess loading file:
+          ac.decodeAudioData(request.response, // success decoding buffer:
+          function (buff) {
+            self.buffer = buff;
+            self.panner.inputChannels(buff.numberOfChannels);
+            if (callback) {
+              callback(self);
+            }
+          }, // error decoding buffer. "e" is undefined in Chrome 11/22/2015
+          function (e) {
+            var err = new CustomError('decodeAudioData', errorTrace, self.url);
+            var msg = 'AudioContext error at decodeAudioData for ' + self.url;
+            if (errorCallback) {
+              err.msg = msg;
+              errorCallback(err);
+            } else {
+              console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+            }
+          });
+        } else {
+          var err = new CustomError('loadSound', errorTrace, self.url);
+          var msg = 'Unable to load ' + self.url + '. The request status was: ' + request.status + ' (' + request.statusText + ')';
+          if (errorCallback) {
+            err.message = msg;
+            errorCallback(err);
+          } else {
+            console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+          }
+        }
+      };
+      // if there is another error, aside from 404...
+      request.onerror = function (e) {
+        var err = new CustomError('loadSound', errorTrace, self.url);
+        var msg = 'There was no response from the server at ' + self.url + '. Check the url and internet connectivity.';
+        if (errorCallback) {
+          err.message = msg;
+          errorCallback(err);
+        } else {
+          console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+        }
+      };
+      request.send();
+    } else if (this.file != undefined) {
+      var reader = new FileReader();
+      var self = this;
+      reader.onload = function () {
+        ac.decodeAudioData(reader.result, function (buff) {
+          self.buffer = buff;
+          self.panner.inputChannels(buff.numberOfChannels);
+          if (callback) {
+            callback(self);
+          }
+        });
+      };
+      reader.onerror = function (e) {
+        if (onerror)
+          onerror(e);
+      };
+      reader.readAsArrayBuffer(this.file);
+    }
+  };
+  // TO DO: use this method to create a loading bar that shows progress during file upload/decode.
+  p5.SoundFile.prototype._updateProgress = function (evt) {
+    if (evt.lengthComputable) {
+      var percentComplete = Math.log(evt.loaded / evt.total * 9.9);
+      this._whileLoading(percentComplete);
+    } else {
+      // Unable to compute progress information since the total size is unknown
+      this._whileLoading('size unknown');
+    }
+  };
+  /**
+   *  Returns true if the sound file finished loading successfully.
+   *  
+   *  @method  isLoaded
+   *  @return {Boolean} 
+   */
+  p5.SoundFile.prototype.isLoaded = function () {
+    if (this.buffer) {
+      return true;
+    } else {
+      return false;
+    }
+  };
+  /**
+   * Play the p5.SoundFile
+   *
+   * @method play
+   * @param {Number} [startTime]            (optional) schedule playback to start (in seconds from now).
+   * @param {Number} [rate]             (optional) playback rate
+   * @param {Number} [amp]              (optional) amplitude (volume)
+   *                                     of playback
+   * @param {Number} [cueStart]        (optional) cue start time in seconds
+   * @param {Number} [duration]          (optional) duration of playback in seconds
+   */
+  p5.SoundFile.prototype.play = function (time, rate, amp, _cueStart, duration) {
+    var self = this;
+    var now = p5sound.audiocontext.currentTime;
+    var cueStart, cueEnd;
+    var time = time || 0;
+    if (time < 0) {
+      time = 0;
+    }
+    time = time + now;
+    // TO DO: if already playing, create array of buffers for easy stop()
+    if (this.buffer) {
+      // reset the pause time (if it was paused)
+      this._pauseTime = 0;
+      // handle restart playmode
+      if (this.mode === 'restart' && this.buffer && this.bufferSourceNode) {
+        var now = p5sound.audiocontext.currentTime;
+        this.bufferSourceNode.stop(time);
+        this._counterNode.stop(time);
+      }
+      // make a new source and counter. They are automatically assigned playbackRate and buffer
+      this.bufferSourceNode = this._initSourceNode();
+      // garbage collect counterNode and create a new one
+      if (this._counterNode)
+        this._counterNode = undefined;
+      this._counterNode = this._initCounterNode();
+      if (_cueStart) {
+        if (_cueStart >= 0 && _cueStart < this.buffer.duration) {
+          // this.startTime = cueStart;
+          cueStart = _cueStart;
+        } else {
+          throw 'start time out of range';
+        }
+      } else {
+        cueStart = 0;
+      }
+      if (duration) {
+        // if duration is greater than buffer.duration, just play entire file anyway rather than throw an error
+        duration = duration <= this.buffer.duration - cueStart ? duration : this.buffer.duration;
+      } else {
+        duration = this.buffer.duration - cueStart;
+      }
+      // TO DO: Fix this. It broke in Safari
+      // 
+      // method of controlling gain for individual bufferSourceNodes, without resetting overall soundfile volume
+      // if (typeof(this.bufferSourceNode.gain === 'undefined' ) ) {
+      //   this.bufferSourceNode.gain = p5sound.audiocontext.createGain();
+      // }
+      // this.bufferSourceNode.connect(this.bufferSourceNode.gain);
+      // set local amp if provided, otherwise 1
+      var a = amp || 1;
+      // this.bufferSourceNode.gain.gain.setValueAtTime(a, p5sound.audiocontext.currentTime);
+      // this.bufferSourceNode.gain.connect(this.output); 
+      this.bufferSourceNode.connect(this.output);
+      this.output.gain.value = a;
+      // not necessary with _initBufferSource ?
+      // this.bufferSourceNode.playbackRate.cancelScheduledValues(now);
+      rate = rate || Math.abs(this.playbackRate);
+      this.bufferSourceNode.playbackRate.setValueAtTime(rate, now);
+      // if it was paused, play at the pause position
+      if (this._paused) {
+        this.bufferSourceNode.start(time, this.pauseTime, duration);
+        this._counterNode.start(time, this.pauseTime, duration);
+      } else {
+        this.bufferSourceNode.start(time, cueStart, duration);
+        this._counterNode.start(time, cueStart, duration);
+      }
+      this._playing = true;
+      this._paused = false;
+      // add source to sources array, which is used in stopAll()
+      this.bufferSourceNodes.push(this.bufferSourceNode);
+      this.bufferSourceNode._arrayIndex = this.bufferSourceNodes.length - 1;
+      // delete this.bufferSourceNode from the sources array when it is done playing:
+      var clearOnEnd = function (e) {
+        this._playing = false;
+        this.removeEventListener('ended', clearOnEnd, false);
+        // call the onended callback
+        self._onended(self);
+        self.bufferSourceNodes.forEach(function (n, i) {
+          if (n._playing === false) {
+            self.bufferSourceNodes.splice(i);
+          }
+        });
+        if (self.bufferSourceNodes.length === 0) {
+          self._playing = false;
+        }
+      };
+      this.bufferSourceNode.onended = clearOnEnd;
+    } else {
+      throw 'not ready to play file, buffer has yet to load. Try preload()';
+    }
+    // if looping, will restart at original time
+    this.bufferSourceNode.loop = this._looping;
+    this._counterNode.loop = this._looping;
+    if (this._looping === true) {
+      var cueEnd = cueStart + duration;
+      this.bufferSourceNode.loopStart = cueStart;
+      this.bufferSourceNode.loopEnd = cueEnd;
+      this._counterNode.loopStart = cueStart;
+      this._counterNode.loopEnd = cueEnd;
+    }
+  };
+  /**
+   *  p5.SoundFile has two play modes: <code>restart</code> and
+   *  <code>sustain</code>. Play Mode determines what happens to a
+   *  p5.SoundFile if it is triggered while in the middle of playback.
+   *  In sustain mode, playback will continue simultaneous to the
+   *  new playback. In restart mode, play() will stop playback
+   *  and start over. Sustain is the default mode. 
+   *  
+   *  @method  playMode
+   *  @param  {String} str 'restart' or 'sustain'
+   *  @example
+   *  <div><code>
+   *  function setup(){
+   *    mySound = loadSound('assets/Damscray_DancingTiger.mp3');
+   *  }
+   *  function mouseClicked() {
+   *    mySound.playMode('sustain');
+   *    mySound.play();
+   *  }
+   *  function keyPressed() {
+   *    mySound.playMode('restart');
+   *    mySound.play();
+   *  }
+   * 
+   * </code></div>
+   */
+  p5.SoundFile.prototype.playMode = function (str) {
+    var s = str.toLowerCase();
+    // if restart, stop all other sounds from playing
+    if (s === 'restart' && this.buffer && this.bufferSourceNode) {
+      for (var i = 0; i < this.bufferSourceNodes.length - 1; i++) {
+        var now = p5sound.audiocontext.currentTime;
+        this.bufferSourceNodes[i].stop(now);
+      }
+    }
+    // set play mode to effect future playback
+    if (s === 'restart' || s === 'sustain') {
+      this.mode = s;
+    } else {
+      throw 'Invalid play mode. Must be either "restart" or "sustain"';
+    }
+  };
+  /**
+   *  Pauses a file that is currently playing. If the file is not
+   *  playing, then nothing will happen.
+   *
+   *  After pausing, .play() will resume from the paused
+   *  position.
+   *  If p5.SoundFile had been set to loop before it was paused,
+   *  it will continue to loop after it is unpaused with .play().
+   *
+   *  @method pause
+   *  @param {Number} [startTime] (optional) schedule event to occur
+   *                               seconds from now
+   *  @example
+   *  <div><code>
+   *  var soundFile;
+   *  
+   *  function preload() {
+   *    soundFormats('ogg', 'mp3');
+   *    soundFile = loadSound('assets/Damscray_-_Dancing_Tiger_02.mp3');
+   *  }
+   *  function setup() {
+   *    background(0, 255, 0);
+   *    soundFile.setVolume(0.1);
+   *    soundFile.loop();
+   *  }
+   *  function keyTyped() {
+   *    if (key == 'p') {
+   *      soundFile.pause();
+   *      background(255, 0, 0);
+   *    }
+   *  }
+   *  
+   *  function keyReleased() {
+   *    if (key == 'p') {
+   *      soundFile.play();
+   *      background(0, 255, 0);
+   *    }
+   *  </code>
+   *  </div>
+   */
+  p5.SoundFile.prototype.pause = function (time) {
+    var now = p5sound.audiocontext.currentTime;
+    var time = time || 0;
+    var pTime = time + now;
+    if (this.isPlaying() && this.buffer && this.bufferSourceNode) {
+      this.pauseTime = this.currentTime();
+      this.bufferSourceNode.stop(pTime);
+      this._counterNode.stop(pTime);
+      this._paused = true;
+      this._playing = false;
+      this._pauseTime = this.currentTime();
+    } else {
+      this._pauseTime = 0;
+    }
+  };
+  /**
+   * Loop the p5.SoundFile. Accepts optional parameters to set the
+   * playback rate, playback volume, loopStart, loopEnd.
+   *
+   * @method loop
+   * @param {Number} [startTime] (optional) schedule event to occur
+   *                             seconds from now
+   * @param {Number} [rate]        (optional) playback rate
+   * @param {Number} [amp]         (optional) playback volume
+   * @param {Number} [cueLoopStart](optional) startTime in seconds
+   * @param {Number} [duration]  (optional) loop duration in seconds
+   */
+  p5.SoundFile.prototype.loop = function (startTime, rate, amp, loopStart, duration) {
+    this._looping = true;
+    this.play(startTime, rate, amp, loopStart, duration);
+  };
+  /**
+   * Set a p5.SoundFile's looping flag to true or false. If the sound
+   * is currently playing, this change will take effect when it
+   * reaches the end of the current playback. 
+   * 
+   * @param {Boolean} Boolean   set looping to true or false
+   */
+  p5.SoundFile.prototype.setLoop = function (bool) {
+    if (bool === true) {
+      this._looping = true;
+    } else if (bool === false) {
+      this._looping = false;
+    } else {
+      throw 'Error: setLoop accepts either true or false';
+    }
+    if (this.bufferSourceNode) {
+      this.bufferSourceNode.loop = this._looping;
+      this._counterNode.loop = this._looping;
+    }
+  };
+  /**
+   * Returns 'true' if a p5.SoundFile is currently looping and playing, 'false' if not.
+   *
+   * @return {Boolean}
+   */
+  p5.SoundFile.prototype.isLooping = function () {
+    if (!this.bufferSourceNode) {
+      return false;
+    }
+    if (this._looping === true && this.isPlaying() === true) {
+      return true;
+    }
+    return false;
+  };
+  /**
+   *  Returns true if a p5.SoundFile is playing, false if not (i.e.
+   *  paused or stopped).
+   *
+   *  @method isPlaying
+   *  @return {Boolean}
+   */
+  p5.SoundFile.prototype.isPlaying = function () {
+    return this._playing;
+  };
+  /**
+   *  Returns true if a p5.SoundFile is paused, false if not (i.e.
+   *  playing or stopped).
+   *
+   *  @method  isPaused
+   *  @return {Boolean}
+   */
+  p5.SoundFile.prototype.isPaused = function () {
+    return this._paused;
+  };
+  /**
+   * Stop soundfile playback.
+   *
+   * @method stop
+   * @param {Number} [startTime] (optional) schedule event to occur
+   *                             in seconds from now
+   */
+  p5.SoundFile.prototype.stop = function (timeFromNow) {
+    var time = timeFromNow || 0;
+    if (this.mode == 'sustain') {
+      this.stopAll(time);
+      this._playing = false;
+      this.pauseTime = 0;
+      this._paused = false;
+    } else if (this.buffer && this.bufferSourceNode) {
+      var now = p5sound.audiocontext.currentTime;
+      var t = time || 0;
+      this.pauseTime = 0;
+      this.bufferSourceNode.stop(now + t);
+      this._counterNode.stop(now + t);
+      this._playing = false;
+      this._paused = false;
+    }
+  };
+  /**
+   *  Stop playback on all of this soundfile's sources.
+   *  @private
+   */
+  p5.SoundFile.prototype.stopAll = function (_time) {
+    var now = p5sound.audiocontext.currentTime;
+    var time = _time || 0;
+    if (this.buffer && this.bufferSourceNode) {
+      for (var i = 0; i < this.bufferSourceNodes.length; i++) {
+        if (typeof this.bufferSourceNodes[i] != undefined) {
+          try {
+            this.bufferSourceNodes[i].onended = function () {
+            };
+            this.bufferSourceNodes[i].stop(now + time);
+          } catch (e) {
+          }
+        }
+      }
+      this._counterNode.stop(now + time);
+      this._onended(this);
+    }
+  };
+  /**
+   *  Multiply the output volume (amplitude) of a sound file
+   *  between 0.0 (silence) and 1.0 (full volume).
+   *  1.0 is the maximum amplitude of a digital sound, so multiplying
+   *  by greater than 1.0 may cause digital distortion. To
+   *  fade, provide a <code>rampTime</code> parameter. For more
+   *  complex fades, see the Env class.
+   *
+   *  Alternately, you can pass in a signal source such as an
+   *  oscillator to modulate the amplitude with an audio signal.
+   *
+   *  @method  setVolume
+   *  @param {Number|Object} volume  Volume (amplitude) between 0.0
+   *                                     and 1.0 or modulating signal/oscillator
+   *  @param {Number} [rampTime]  Fade for t seconds
+   *  @param {Number} [timeFromNow]  Schedule this event to happen at
+   *                                 t seconds in the future
+   */
+  p5.SoundFile.prototype.setVolume = function (vol, rampTime, tFromNow) {
+    if (typeof vol === 'number') {
+      var rampTime = rampTime || 0;
+      var tFromNow = tFromNow || 0;
+      var now = p5sound.audiocontext.currentTime;
+      var currentVol = this.output.gain.value;
+      this.output.gain.cancelScheduledValues(now + tFromNow);
+      this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
+      this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
+    } else if (vol) {
+      vol.connect(this.output.gain);
+    } else {
+      // return the Gain Node
+      return this.output.gain;
+    }
+  };
+  // same as setVolume, to match Processing Sound
+  p5.SoundFile.prototype.amp = p5.SoundFile.prototype.setVolume;
+  // these are the same thing
+  p5.SoundFile.prototype.fade = p5.SoundFile.prototype.setVolume;
+  p5.SoundFile.prototype.getVolume = function () {
+    return this.output.gain.value;
+  };
+  /**
+   * Set the stereo panning of a p5.sound object to
+   * a floating point number between -1.0 (left) and 1.0 (right).
+   * Default is 0.0 (center).
+   *
+   * @method pan
+   * @param {Number} [panValue]     Set the stereo panner
+   * @param  {Number} timeFromNow schedule this event to happen
+   *                                seconds from now
+   * @example
+   * <div><code>
+   *
+   *  var ball = {};
+   *  var soundFile;
+   *
+   *  function setup() {
+   *    soundFormats('ogg', 'mp3');
+   *    soundFile = loadSound('assets/beatbox.mp3');
+   *  }
+   *  
+   *  function draw() {
+   *    background(0);
+   *    ball.x = constrain(mouseX, 0, width);
+   *    ellipse(ball.x, height/2, 20, 20)
+   *  }
+   *  
+   *  function mousePressed(){
+   *    // map the ball's x location to a panning degree 
+   *    // between -1.0 (left) and 1.0 (right)
+   *    var panning = map(ball.x, 0., width,-1.0, 1.0);
+   *    soundFile.pan(panning);
+   *    soundFile.play();
+   *  }
+   *  </div></code>
+   */
+  p5.SoundFile.prototype.pan = function (pval, tFromNow) {
+    this.panPosition = pval;
+    this.panner.pan(pval, tFromNow);
+  };
+  /**
+   * Returns the current stereo pan position (-1.0 to 1.0)
+   *
+   * @return {Number} Returns the stereo pan setting of the Oscillator
+   *                          as a number between -1.0 (left) and 1.0 (right).
+   *                          0.0 is center and default.
+   */
+  p5.SoundFile.prototype.getPan = function () {
+    return this.panPosition;
+  };
+  /**
+   *  Set the playback rate of a sound file. Will change the speed and the pitch.
+   *  Values less than zero will reverse the audio buffer.
+   *
+   *  @method rate
+   *  @param {Number} [playbackRate]     Set the playback rate. 1.0 is normal,
+   *                                     .5 is half-speed, 2.0 is twice as fast.
+   *                                     Must be greater than zero.
+   *  @example
+   *  <div><code>
+   *  var song;
+   *  
+   *  function preload() {
+   *    song = loadSound('assets/Damscray_DancingTiger.mp3');
+   *  }
+   *
+   *  function setup() {
+   *    song.loop();
+   *  }
+   *
+   *  function draw() {
+   *    background(200);
+   *    
+   *    // Set the rate to a range between 0.1 and 4
+   *    // Changing the rate also alters the pitch
+   *    var speed = map(mouseY, 0.1, height, 0, 2);
+   *    speed = constrain(speed, 0.01, 4);
+   *    song.rate(speed);
+   *    
+   *    // Draw a circle to show what is going on
+   *    stroke(0);
+   *    fill(51, 100);
+   *    ellipse(mouseX, 100, 48, 48);
+   *  }
+   *  
+   * </code>
+   * </div>
+   *  
+   */
+  p5.SoundFile.prototype.rate = function (playbackRate) {
+    if (this.playbackRate === playbackRate && this.bufferSourceNode) {
+      if (this.bufferSourceNode.playbackRate.value === playbackRate) {
+        return;
+      }
+    }
+    this.playbackRate = playbackRate;
+    var rate = playbackRate;
+    if (this.playbackRate === 0 && this._playing) {
+      this.pause();
+    }
+    if (this.playbackRate < 0 && !this.reversed) {
+      var cPos = this.currentTime();
+      var cRate = this.bufferSourceNode.playbackRate.value;
+      // this.pause();
+      this.reverseBuffer();
+      rate = Math.abs(playbackRate);
+      var newPos = (cPos - this.duration()) / rate;
+      this.pauseTime = newPos;
+    } else if (this.playbackRate > 0 && this.reversed) {
+      this.reverseBuffer();
+    }
+    if (this.bufferSourceNode) {
+      var now = p5sound.audiocontext.currentTime;
+      this.bufferSourceNode.playbackRate.cancelScheduledValues(now);
+      this.bufferSourceNode.playbackRate.linearRampToValueAtTime(Math.abs(rate), now);
+      this._counterNode.playbackRate.cancelScheduledValues(now);
+      this._counterNode.playbackRate.linearRampToValueAtTime(Math.abs(rate), now);
+    }
+  };
+  // TO DO: document this
+  p5.SoundFile.prototype.setPitch = function (num) {
+    var newPlaybackRate = midiToFreq(num) / midiToFreq(60);
+    this.rate(newPlaybackRate);
+  };
+  p5.SoundFile.prototype.getPlaybackRate = function () {
+    return this.playbackRate;
+  };
+  /**
+   * Returns the duration of a sound file in seconds.
+   *
+   * @method duration
+   * @return {Number} The duration of the soundFile in seconds.
+   */
+  p5.SoundFile.prototype.duration = function () {
+    // Return Duration
+    if (this.buffer) {
+      return this.buffer.duration;
+    } else {
+      return 0;
+    }
+  };
+  /**
+   * Return the current position of the p5.SoundFile playhead, in seconds.
+   * Note that if you change the playbackRate while the p5.SoundFile is
+   * playing, the results may not be accurate.
+   *
+   * @method currentTime
+   * @return {Number}   currentTime of the soundFile in seconds.
+   */
+  p5.SoundFile.prototype.currentTime = function () {
+    // TO DO --> make reverse() flip these values appropriately
+    if (this._pauseTime > 0) {
+      return this._pauseTime;
+    } else {
+      return this._lastPos / ac.sampleRate;
+    }
+  };
+  /**
+   * Move the playhead of the song to a position, in seconds. Start
+   * and Stop time. If none are given, will reset the file to play
+   * entire duration from start to finish.
+   *
+   * @method jump
+   * @param {Number} cueTime    cueTime of the soundFile in seconds.
+   * @param {Number} uuration    duration in seconds.
+   */
+  p5.SoundFile.prototype.jump = function (cueTime, duration) {
+    if (cueTime < 0 || cueTime > this.buffer.duration) {
+      throw 'jump time out of range';
+    }
+    if (duration > this.buffer.duration - cueTime) {
+      throw 'end time out of range';
+    }
+    var cTime = cueTime || 0;
+    var eTime = duration || this.buffer.duration - cueTime;
+    if (this.isPlaying()) {
+      this.stop();
+    }
+    this.play(0, this.playbackRate, this.output.gain.value, cTime, eTime);
+  };
+  /**
+  * Return the number of channels in a sound file.
+  * For example, Mono = 1, Stereo = 2.
+  *
+  * @method channels
+  * @return {Number} [channels]
+  */
+  p5.SoundFile.prototype.channels = function () {
+    return this.buffer.numberOfChannels;
+  };
+  /**
+  * Return the sample rate of the sound file.
+  *
+  * @method sampleRate
+  * @return {Number} [sampleRate]
+  */
+  p5.SoundFile.prototype.sampleRate = function () {
+    return this.buffer.sampleRate;
+  };
+  /**
+  * Return the number of samples in a sound file.
+  * Equal to sampleRate * duration.
+  *
+  * @method frames
+  * @return {Number} [sampleCount]
+  */
+  p5.SoundFile.prototype.frames = function () {
+    return this.buffer.length;
+  };
+  /**
+   * Returns an array of amplitude peaks in a p5.SoundFile that can be
+   * used to draw a static waveform. Scans through the p5.SoundFile's
+   * audio buffer to find the greatest amplitudes. Accepts one
+   * parameter, 'length', which determines size of the array.
+   * Larger arrays result in more precise waveform visualizations.
+   * 
+   * Inspired by Wavesurfer.js.
+   * 
+   * @method  getPeaks
+   * @params {Number} [length] length is the size of the returned array.
+   *                          Larger length results in more precision.
+   *                          Defaults to 5*width of the browser window.
+   * @returns {Float32Array} Array of peaks.
+   */
+  p5.SoundFile.prototype.getPeaks = function (length) {
+    if (this.buffer) {
+      // set length to window's width if no length is provided
+      if (!length) {
+        length = window.width * 5;
+      }
+      if (this.buffer) {
+        var buffer = this.buffer;
+        var sampleSize = buffer.length / length;
+        var sampleStep = ~~(sampleSize / 10) || 1;
+        var channels = buffer.numberOfChannels;
+        var peaks = new Float32Array(Math.round(length));
+        for (var c = 0; c < channels; c++) {
+          var chan = buffer.getChannelData(c);
+          for (var i = 0; i < length; i++) {
+            var start = ~~(i * sampleSize);
+            var end = ~~(start + sampleSize);
+            var max = 0;
+            for (var j = start; j < end; j += sampleStep) {
+              var value = chan[j];
+              if (value > max) {
+                max = value;
+              } else if (-value > max) {
+                max = value;
+              }
+            }
+            if (c === 0 || Math.abs(max) > peaks[i]) {
+              peaks[i] = max;
+            }
+          }
+        }
+        return peaks;
+      }
+    } else {
+      throw 'Cannot load peaks yet, buffer is not loaded';
+    }
+  };
+  /**
+   *  Reverses the p5.SoundFile's buffer source.
+   *  Playback must be handled separately (see example).
+   *
+   *  @method  reverseBuffer
+   *  @example
+   *  <div><code>
+   *  var drum;
+   *  
+   *  function preload() {
+   *    drum = loadSound('assets/drum.mp3');
+   *  }
+   *
+   *  function setup() {
+   *    drum.reverseBuffer();
+   *    drum.play();
+   *  }
+   *  
+   * </code>
+   * </div>
+   */
+  p5.SoundFile.prototype.reverseBuffer = function () {
+    var curVol = this.getVolume();
+    this.setVolume(0, 0.01, 0);
+    this.pause();
+    if (this.buffer) {
+      for (var i = 0; i < this.buffer.numberOfChannels; i++) {
+        Array.prototype.reverse.call(this.buffer.getChannelData(i));
+      }
+      // set reversed flag
+      this.reversed = !this.reversed;
+    } else {
+      throw 'SoundFile is not done loading';
+    }
+    this.setVolume(curVol, 0.01, 0.0101);
+    this.play();
+  };
+  /**
+   *  Schedule an event to be called when the soundfile
+   *  reaches the end of a buffer. If the soundfile is
+   *  playing through once, this will be called when it
+   *  ends. If it is looping, it will be called when
+   *  stop is called.
+   *  
+   *  @method  onended
+   *  @param  {Function} callback function to call when the
+   *                              soundfile has ended.
+   */
+  p5.SoundFile.prototype.onended = function (callback) {
+    this._onended = callback;
+    return this;
+  };
+  p5.SoundFile.prototype.add = function () {
+  };
+  p5.SoundFile.prototype.dispose = function () {
+    var now = p5sound.audiocontext.currentTime;
+    // remove reference to soundfile
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this.stop(now);
+    if (this.buffer && this.bufferSourceNode) {
+      for (var i = 0; i < this.bufferSourceNodes.length - 1; i++) {
+        if (this.bufferSourceNodes[i] !== null) {
+          this.bufferSourceNodes[i].disconnect();
+          try {
+            this.bufferSourceNodes[i].stop(now);
+          } catch (e) {
+          }
+          this.bufferSourceNodes[i] = null;
+        }
+      }
+      if (this.isPlaying()) {
+        try {
+          this._counterNode.stop(now);
+        } catch (e) {
+          console.log(e);
+        }
+        this._counterNode = null;
+      }
+    }
+    if (this.output) {
+      this.output.disconnect();
+      this.output = null;
+    }
+    if (this.panner) {
+      this.panner.disconnect();
+      this.panner = null;
+    }
+  };
+  /**
+   * Connects the output of a p5sound object to input of another
+   * p5.sound object. For example, you may connect a p5.SoundFile to an
+   * FFT or an Effect. If no parameter is given, it will connect to
+   * the master output. Most p5sound objects connect to the master
+   * output when they are created.
+   *
+   * @method connect
+   * @param {Object} [object] Audio object that accepts an input
+   */
+  p5.SoundFile.prototype.connect = function (unit) {
+    if (!unit) {
+      this.panner.connect(p5sound.input);
+    } else {
+      if (unit.hasOwnProperty('input')) {
+        this.panner.connect(unit.input);
+      } else {
+        this.panner.connect(unit);
+      }
+    }
+  };
+  /**
+   * Disconnects the output of this p5sound object.
+   *
+   * @method disconnect
+   */
+  p5.SoundFile.prototype.disconnect = function (unit) {
+    this.panner.disconnect(unit);
+  };
+  /**
+   */
+  p5.SoundFile.prototype.getLevel = function (smoothing) {
+    console.warn('p5.SoundFile.getLevel has been removed from the library. Use p5.Amplitude instead');
+  };
+  /**
+   *  Reset the source for this SoundFile to a
+   *  new path (URL).
+   *
+   *  @method  setPath
+   *  @param {String}   path     path to audio file
+   *  @param {Function} callback Callback
+   */
+  p5.SoundFile.prototype.setPath = function (p, callback) {
+    var path = p5.prototype._checkFileFormats(p);
+    this.url = path;
+    this.load(callback);
+  };
+  /**
+   *  Replace the current Audio Buffer with a new Buffer.
+   *  
+   *  @param {Array} buf Array of Float32 Array(s). 2 Float32 Arrays
+   *                     will create a stereo source. 1 will create
+   *                     a mono source.
+   */
+  p5.SoundFile.prototype.setBuffer = function (buf) {
+    var numChannels = buf.length;
+    var size = buf[0].length;
+    var newBuffer = ac.createBuffer(numChannels, size, ac.sampleRate);
+    if (!buf[0] instanceof Float32Array) {
+      buf[0] = new Float32Array(buf[0]);
+    }
+    for (var channelNum = 0; channelNum < numChannels; channelNum++) {
+      var channel = newBuffer.getChannelData(channelNum);
+      channel.set(buf[channelNum]);
+    }
+    this.buffer = newBuffer;
+    // set numbers of channels on input to the panner
+    this.panner.inputChannels(numChannels);
+  };
+  //////////////////////////////////////////////////
+  // script processor node with an empty buffer to help
+  // keep a sample-accurate position in playback buffer.
+  // Inspired by Chinmay Pendharkar's technique for Sonoport --> http://bit.ly/1HwdCsV
+  // Copyright [2015] [Sonoport (Asia) Pte. Ltd.],
+  // Licensed under the Apache License http://apache.org/licenses/LICENSE-2.0
+  ////////////////////////////////////////////////////////////////////////////////////
+  // initialize counterNode, set its initial buffer and playbackRate
+  p5.SoundFile.prototype._initCounterNode = function () {
+    var self = this;
+    var now = ac.currentTime;
+    var cNode = ac.createBufferSource();
+    // dispose of scope node if it already exists
+    if (self._scopeNode) {
+      self._scopeNode.disconnect();
+      self._scopeNode.onaudioprocess = undefined;
+      self._scopeNode = null;
+    }
+    self._scopeNode = ac.createScriptProcessor(256, 1, 1);
+    // create counter buffer of the same length as self.buffer
+    cNode.buffer = _createCounterBuffer(self.buffer);
+    cNode.playbackRate.setValueAtTime(self.playbackRate, now);
+    cNode.connect(self._scopeNode);
+    self._scopeNode.connect(p5.soundOut._silentNode);
+    self._scopeNode.onaudioprocess = function (processEvent) {
+      var inputBuffer = processEvent.inputBuffer.getChannelData(0);
+      // update the lastPos
+      self._lastPos = inputBuffer[inputBuffer.length - 1] || 0;
+      // do any callbacks that have been scheduled
+      self._onTimeUpdate(self._lastPos);
+    };
+    return cNode;
+  };
+  // initialize sourceNode, set its initial buffer and playbackRate
+  p5.SoundFile.prototype._initSourceNode = function () {
+    var self = this;
+    var now = ac.currentTime;
+    var bufferSourceNode = ac.createBufferSource();
+    bufferSourceNode.buffer = self.buffer;
+    bufferSourceNode.playbackRate.setValueAtTime(self.playbackRate, now);
+    return bufferSourceNode;
+  };
+  var _createCounterBuffer = function (buffer) {
+    var array = new Float32Array(buffer.length);
+    var audioBuf = ac.createBuffer(1, buffer.length, 44100);
+    for (var index = 0; index < buffer.length; index++) {
+      array[index] = index;
+    }
+    audioBuf.getChannelData(0).set(array);
+    return audioBuf;
+  };
+  /**
+   *  processPeaks returns an array of timestamps where it thinks there is a beat.
+   *
+   *  This is an asynchronous function that processes the soundfile in an offline audio context,
+   *  and sends the results to your callback function.
+   *
+   *  The process involves running the soundfile through a lowpass filter, and finding all of the
+   *  peaks above the initial threshold. If the total number of peaks are below the minimum number of peaks,
+   *  it decreases the threshold and re-runs the analysis until either minPeaks or minThreshold are reached.
+   *  
+   *  @method  processPeaks
+   *  @param  {Function} callback       a function to call once this data is returned
+   *  @param  {Number}   [initThreshold] initial threshold defaults to 0.9
+   *  @param  {Number}   [minThreshold]   minimum threshold defaults to 0.22
+   *  @param  {Number}   [minPeaks]       minimum number of peaks defaults to 200
+   *  @return {Array}                  Array of timestamped peaks
+   */
+  p5.SoundFile.prototype.processPeaks = function (callback, _initThreshold, _minThreshold, _minPeaks) {
+    var bufLen = this.buffer.length;
+    var sampleRate = this.buffer.sampleRate;
+    var buffer = this.buffer;
+    var initialThreshold = _initThreshold || 0.9, threshold = initialThreshold, minThreshold = _minThreshold || 0.22, minPeaks = _minPeaks || 200;
+    // Create offline context
+    var offlineContext = new OfflineAudioContext(1, bufLen, sampleRate);
+    // create buffer source
+    var source = offlineContext.createBufferSource();
+    source.buffer = buffer;
+    // Create filter. TO DO: allow custom setting of filter
+    var filter = offlineContext.createBiquadFilter();
+    filter.type = 'lowpass';
+    source.connect(filter);
+    filter.connect(offlineContext.destination);
+    // start playing at time:0
+    source.start(0);
+    offlineContext.startRendering();
+    // Render the song
+    // act on the result
+    offlineContext.oncomplete = function (e) {
+      var data = {};
+      var filteredBuffer = e.renderedBuffer;
+      var bufferData = filteredBuffer.getChannelData(0);
+      // step 1: 
+      // create Peak instances, add them to array, with strength and sampleIndex
+      do {
+        allPeaks = getPeaksAtThreshold(bufferData, threshold);
+        threshold -= 0.005;
+      } while (Object.keys(allPeaks).length < minPeaks && threshold >= minThreshold);
+      // step 2:
+      // find intervals for each peak in the sampleIndex, add tempos array
+      var intervalCounts = countIntervalsBetweenNearbyPeaks(allPeaks);
+      // step 3: find top tempos
+      var groups = groupNeighborsByTempo(intervalCounts, filteredBuffer.sampleRate);
+      // sort top intervals
+      var topTempos = groups.sort(function (intA, intB) {
+        return intB.count - intA.count;
+      }).splice(0, 5);
+      // set this SoundFile's tempo to the top tempo ??
+      this.tempo = topTempos[0].tempo;
+      // step 4:
+      // new array of peaks at top tempo within a bpmVariance
+      var bpmVariance = 5;
+      var tempoPeaks = getPeaksAtTopTempo(allPeaks, topTempos[0].tempo, filteredBuffer.sampleRate, bpmVariance);
+      callback(tempoPeaks);
+    };
+  };
+  // process peaks
+  var Peak = function (amp, i) {
+    this.sampleIndex = i;
+    this.amplitude = amp;
+    this.tempos = [];
+    this.intervals = [];
+  };
+  var allPeaks = [];
+  // 1. for processPeaks() Function to identify peaks above a threshold
+  // returns an array of peak indexes as frames (samples) of the original soundfile
+  function getPeaksAtThreshold(data, threshold) {
+    var peaksObj = {};
+    var length = data.length;
+    for (var i = 0; i < length; i++) {
+      if (data[i] > threshold) {
+        var amp = data[i];
+        var peak = new Peak(amp, i);
+        peaksObj[i] = peak;
+        // Skip forward ~ 1/8s to get past this peak.
+        i += 6000;
+      }
+      i++;
+    }
+    return peaksObj;
+  }
+  // 2. for processPeaks()
+  function countIntervalsBetweenNearbyPeaks(peaksObj) {
+    var intervalCounts = [];
+    var peaksArray = Object.keys(peaksObj).sort();
+    for (var index = 0; index < peaksArray.length; index++) {
+      // find intervals in comparison to nearby peaks
+      for (var i = 0; i < 10; i++) {
+        var startPeak = peaksObj[peaksArray[index]];
+        var endPeak = peaksObj[peaksArray[index + i]];
+        if (startPeak && endPeak) {
+          var startPos = startPeak.sampleIndex;
+          var endPos = endPeak.sampleIndex;
+          var interval = endPos - startPos;
+          // add a sample interval to the startPeek in the allPeaks array
+          if (interval > 0) {
+            startPeak.intervals.push(interval);
+          }
+          // tally the intervals and return interval counts
+          var foundInterval = intervalCounts.some(function (intervalCount, p) {
+            if (intervalCount.interval === interval) {
+              intervalCount.count++;
+              return intervalCount;
+            }
+          });
+          // store with JSON like formatting
+          if (!foundInterval) {
+            intervalCounts.push({
+              interval: interval,
+              count: 1
+            });
+          }
+        }
+      }
+    }
+    return intervalCounts;
+  }
+  // 3. for processPeaks --> find tempo
+  function groupNeighborsByTempo(intervalCounts, sampleRate) {
+    var tempoCounts = [];
+    intervalCounts.forEach(function (intervalCount, i) {
+      try {
+        // Convert an interval to tempo
+        var theoreticalTempo = Math.abs(60 / (intervalCount.interval / sampleRate));
+        theoreticalTempo = mapTempo(theoreticalTempo);
+        var foundTempo = tempoCounts.some(function (tempoCount) {
+          if (tempoCount.tempo === theoreticalTempo)
+            return tempoCount.count += intervalCount.count;
+        });
+        if (!foundTempo) {
+          if (isNaN(theoreticalTempo)) {
+            return;
+          }
+          tempoCounts.push({
+            tempo: Math.round(theoreticalTempo),
+            count: intervalCount.count
+          });
+        }
+      } catch (e) {
+        throw e;
+      }
+    });
+    return tempoCounts;
+  }
+  // 4. for processPeaks - get peaks at top tempo
+  function getPeaksAtTopTempo(peaksObj, tempo, sampleRate, bpmVariance) {
+    var peaksAtTopTempo = [];
+    var peaksArray = Object.keys(peaksObj).sort();
+    // TO DO: filter out peaks that have the tempo and return
+    for (var i = 0; i < peaksArray.length; i++) {
+      var key = peaksArray[i];
+      var peak = peaksObj[key];
+      for (var j = 0; j < peak.intervals.length; j++) {
+        var intervalBPM = Math.round(Math.abs(60 / (peak.intervals[j] / sampleRate)));
+        intervalBPM = mapTempo(intervalBPM);
+        var dif = intervalBPM - tempo;
+        if (Math.abs(intervalBPM - tempo) < bpmVariance) {
+          // convert sampleIndex to seconds
+          peaksAtTopTempo.push(peak.sampleIndex / 44100);
+        }
+      }
+    }
+    // filter out peaks that are very close to each other
+    peaksAtTopTempo = peaksAtTopTempo.filter(function (peakTime, index, arr) {
+      var dif = arr[index + 1] - peakTime;
+      if (dif > 0.01) {
+        return true;
+      }
+    });
+    return peaksAtTopTempo;
+  }
+  // helper function for processPeaks
+  function mapTempo(theoreticalTempo) {
+    // these scenarios create infinite while loop
+    if (!isFinite(theoreticalTempo) || theoreticalTempo == 0) {
+      return;
+    }
+    // Adjust the tempo to fit within the 90-180 BPM range
+    while (theoreticalTempo < 90)
+      theoreticalTempo *= 2;
+    while (theoreticalTempo > 180 && theoreticalTempo > 90)
+      theoreticalTempo /= 2;
+    return theoreticalTempo;
+  }
+  /*** SCHEDULE EVENTS ***/
+  /**
+   *  Schedule events to trigger every time a MediaElement
+   *  (audio/video) reaches a playback cue point.
+   *
+   *  Accepts a callback function, a time (in seconds) at which to trigger
+   *  the callback, and an optional parameter for the callback.
+   *
+   *  Time will be passed as the first parameter to the callback function,
+   *  and param will be the second parameter.
+   *
+   *
+   *  @method  addCue
+   *  @param {Number}   time     Time in seconds, relative to this media
+   *                             element's playback. For example, to trigger
+   *                             an event every time playback reaches two
+   *                             seconds, pass in the number 2. This will be
+   *                             passed as the first parameter to
+   *                             the callback function.
+   *  @param {Function} callback Name of a function that will be
+   *                             called at the given time. The callback will
+   *                             receive time and (optionally) param as its
+   *                             two parameters.
+   *  @param {Object} [value]    An object to be passed as the
+   *                             second parameter to the
+   *                             callback function.
+   *  @return {Number} id ID of this cue,
+   *                      useful for removeCue(id)
+   *  @example
+   *  <div><code>
+   *  function setup() {
+   *    background(0);
+   *    noStroke();
+   *    fill(255);
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *    
+   *    mySound = loadSound('assets/beat.mp3');
+   *
+   *    // schedule calls to changeText
+   *    mySound.addCue(0.50, changeText, "hello" );
+   *    mySound.addCue(1.00, changeText, "p5" );
+   *    mySound.addCue(1.50, changeText, "what" );
+   *    mySound.addCue(2.00, changeText, "do" );
+   *    mySound.addCue(2.50, changeText, "you" );
+   *    mySound.addCue(3.00, changeText, "want" );
+   *    mySound.addCue(4.00, changeText, "to" );
+   *    mySound.addCue(5.00, changeText, "make" );
+   *    mySound.addCue(6.00, changeText, "?" );
+   *  }
+   *
+   *  function changeText(val) {
+   *    background(0);
+   *    text(val, width/2, height/2);
+   *  }
+   *
+   *  function mouseClicked() {
+   *    if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+   *      if (mySound.isPlaying() ) {
+   *        mySound.stop();
+   *      } else {
+   *        mySound.play();
+   *      }
+   *    }
+   *  }
+   *  </code></div>
+   */
+  p5.SoundFile.prototype.addCue = function (time, callback, val) {
+    var id = this._cueIDCounter++;
+    var cue = new Cue(callback, time, id, val);
+    this._cues.push(cue);
+    // if (!this.elt.ontimeupdate) {
+    //   this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
+    // }
+    return id;
+  };
+  /**
+   *  Remove a callback based on its ID. The ID is returned by the
+   *  addCue method.
+   *
+   *  @method removeCue
+   *  @param  {Number} id ID of the cue, as returned by addCue
+   */
+  p5.SoundFile.prototype.removeCue = function (id) {
+    var cueLength = this._cues.length;
+    for (var i = 0; i < cueLength; i++) {
+      var cue = this._cues[i];
+      if (cue.id === id) {
+        this.cues.splice(i, 1);
+      }
+    }
+    if (this._cues.length === 0) {
+    }
+  };
+  /**
+   *  Remove all of the callbacks that had originally been scheduled
+   *  via the addCue method.
+   *
+   *  @method  clearCues
+   */
+  p5.SoundFile.prototype.clearCues = function () {
+    this._cues = [];
+  };
+  // private method that checks for cues to be fired if events
+  // have been scheduled using addCue(callback, time).
+  p5.SoundFile.prototype._onTimeUpdate = function (position) {
+    var playbackTime = position / this.buffer.sampleRate;
+    var cueLength = this._cues.length;
+    for (var i = 0; i < cueLength; i++) {
+      var cue = this._cues[i];
+      var callbackTime = cue.time;
+      var val = cue.val;
+      if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
+        // pass the scheduled callbackTime as parameter to the callback
+        cue.callback(val);
+      }
+    }
+    this._prevTime = playbackTime;
+  };
+  // Cue inspired by JavaScript setTimeout, and the
+  // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
+  var Cue = function (callback, time, id, val) {
+    this.callback = callback;
+    this.time = time;
+    this.id = id;
+    this.val = val;
+  };
+}(sndcore, errorHandler, master);
+var amplitude;
+amplitude = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+   *  Amplitude measures volume between 0.0 and 1.0.
+   *  Listens to all p5sound by default, or use setInput()
+   *  to listen to a specific sound source. Accepts an optional
+   *  smoothing value, which defaults to 0. 
+   *
+   *  @class p5.Amplitude
+   *  @constructor
+   *  @param {Number} [smoothing] between 0.0 and .999 to smooth
+   *                             amplitude readings (defaults to 0)
+   *  @return {Object}    Amplitude Object
+   *  @example
+   *  <div><code>
+   *  var sound, amplitude, cnv;
+   *  
+   *  function preload(){
+   *    sound = loadSound('assets/beat.mp3');
+   *  }
+   *  function setup() {
+   *    cnv = createCanvas(100,100);
+   *    amplitude = new p5.Amplitude();
+   *
+   *    // start / stop the sound when canvas is clicked
+   *    cnv.mouseClicked(function() {
+   *      if (sound.isPlaying() ){
+   *        sound.stop();
+   *      } else {
+   *        sound.play();
+   *      }
+   *    });
+   *  }
+   *  function draw() {
+   *    background(0);
+   *    fill(255);
+   *    var level = amplitude.getLevel();
+   *    var size = map(level, 0, 1, 0, 200);
+   *    ellipse(width/2, height/2, size, size);
+   *  }
+   *
+   *  </code></div>
+   */
+  p5.Amplitude = function (smoothing) {
+    // Set to 2048 for now. In future iterations, this should be inherited or parsed from p5sound's default
+    this.bufferSize = 2048;
+    // set audio context
+    this.audiocontext = p5sound.audiocontext;
+    this.processor = this.audiocontext.createScriptProcessor(this.bufferSize, 2, 1);
+    // for connections
+    this.input = this.processor;
+    this.output = this.audiocontext.createGain();
+    // smoothing defaults to 0
+    this.smoothing = smoothing || 0;
+    // the variables to return
+    this.volume = 0;
+    this.average = 0;
+    this.stereoVol = [
+      0,
+      0
+    ];
+    this.stereoAvg = [
+      0,
+      0
+    ];
+    this.stereoVolNorm = [
+      0,
+      0
+    ];
+    this.volMax = 0.001;
+    this.normalize = false;
+    this.processor.onaudioprocess = this._audioProcess.bind(this);
+    this.processor.connect(this.output);
+    this.output.gain.value = 0;
+    // this may only be necessary because of a Chrome bug
+    this.output.connect(this.audiocontext.destination);
+    // connect to p5sound master output by default, unless set by input()
+    p5sound.meter.connect(this.processor);
+    // add this p5.SoundFile to the soundArray
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Connects to the p5sound instance (master output) by default.
+   *  Optionally, you can pass in a specific source (i.e. a soundfile).
+   *
+   *  @method setInput
+   *  @param {soundObject|undefined} [snd] set the sound source
+   *                                       (optional, defaults to
+   *                                       master output)
+   *  @param {Number|undefined} [smoothing] a range between 0.0 and 1.0
+   *                                        to smooth amplitude readings
+   *  @example
+   *  <div><code>
+   *  function preload(){
+   *    sound1 = loadSound('assets/beat.mp3');
+   *    sound2 = loadSound('assets/drum.mp3');
+   *  }
+   *  function setup(){
+   *    amplitude = new p5.Amplitude();
+   *    sound1.play();
+   *    sound2.play();
+   *    amplitude.setInput(sound2);
+   *  }
+   *  function draw() {
+   *    background(0);
+   *    fill(255);
+   *    var level = amplitude.getLevel();
+   *    var size = map(level, 0, 1, 0, 200);
+   *    ellipse(width/2, height/2, size, size);
+   *  }
+   *  function mouseClicked(){
+   *    sound1.stop();
+   *    sound2.stop();
+   *  }
+   *  </code></div>
+   */
+  p5.Amplitude.prototype.setInput = function (source, smoothing) {
+    p5sound.meter.disconnect();
+    if (smoothing) {
+      this.smoothing = smoothing;
+    }
+    // connect to the master out of p5s instance if no snd is provided
+    if (source == null) {
+      console.log('Amplitude input source is not ready! Connecting to master output instead');
+      p5sound.meter.connect(this.processor);
+    } else if (source instanceof p5.Signal) {
+      source.output.connect(this.processor);
+    } else if (source) {
+      source.connect(this.processor);
+      this.processor.disconnect();
+      this.processor.connect(this.output);
+    } else {
+      p5sound.meter.connect(this.processor);
+    }
+  };
+  p5.Amplitude.prototype.connect = function (unit) {
+    if (unit) {
+      if (unit.hasOwnProperty('input')) {
+        this.output.connect(unit.input);
+      } else {
+        this.output.connect(unit);
+      }
+    } else {
+      this.output.connect(this.panner.connect(p5sound.input));
+    }
+  };
+  p5.Amplitude.prototype.disconnect = function (unit) {
+    this.output.disconnect();
+  };
+  // TO DO make this stereo / dependent on # of audio channels
+  p5.Amplitude.prototype._audioProcess = function (event) {
+    for (var channel = 0; channel < event.inputBuffer.numberOfChannels; channel++) {
+      var inputBuffer = event.inputBuffer.getChannelData(channel);
+      var bufLength = inputBuffer.length;
+      var total = 0;
+      var sum = 0;
+      var x;
+      for (var i = 0; i < bufLength; i++) {
+        x = inputBuffer[i];
+        if (this.normalize) {
+          total += Math.max(Math.min(x / this.volMax, 1), -1);
+          sum += Math.max(Math.min(x / this.volMax, 1), -1) * Math.max(Math.min(x / this.volMax, 1), -1);
+        } else {
+          total += x;
+          sum += x * x;
+        }
+      }
+      var average = total / bufLength;
+      // ... then take the square root of the sum.
+      var rms = Math.sqrt(sum / bufLength);
+      this.stereoVol[channel] = Math.max(rms, this.stereoVol[channel] * this.smoothing);
+      this.stereoAvg[channel] = Math.max(average, this.stereoVol[channel] * this.smoothing);
+      this.volMax = Math.max(this.stereoVol[channel], this.volMax);
+    }
+    // add volume from all channels together
+    var self = this;
+    var volSum = this.stereoVol.reduce(function (previousValue, currentValue, index) {
+      self.stereoVolNorm[index - 1] = Math.max(Math.min(self.stereoVol[index - 1] / self.volMax, 1), 0);
+      self.stereoVolNorm[index] = Math.max(Math.min(self.stereoVol[index] / self.volMax, 1), 0);
+      return previousValue + currentValue;
+    });
+    // volume is average of channels
+    this.volume = volSum / this.stereoVol.length;
+    // normalized value
+    this.volNorm = Math.max(Math.min(this.volume / this.volMax, 1), 0);
+  };
+  /**
+   *  Returns a single Amplitude reading at the moment it is called.
+   *  For continuous readings, run in the draw loop.
+   *
+   *  @method getLevel
+   *  @param {Number} [channel] Optionally return only channel 0 (left) or 1 (right)
+   *  @return {Number}       Amplitude as a number between 0.0 and 1.0
+   *  @example
+   *  <div><code>
+   *  function preload(){
+   *    sound = loadSound('assets/beat.mp3');
+   *  }
+   *  function setup() { 
+   *    amplitude = new p5.Amplitude();
+   *    sound.play();
+   *  }
+   *  function draw() {
+   *    background(0);
+   *    fill(255);
+   *    var level = amplitude.getLevel();
+   *    var size = map(level, 0, 1, 0, 200);
+   *    ellipse(width/2, height/2, size, size);
+   *  }
+   *  function mouseClicked(){
+   *    sound.stop();
+   *  }
+   *  </code></div>
+   */
+  p5.Amplitude.prototype.getLevel = function (channel) {
+    if (typeof channel !== 'undefined') {
+      if (this.normalize) {
+        return this.stereoVolNorm[channel];
+      } else {
+        return this.stereoVol[channel];
+      }
+    } else if (this.normalize) {
+      return this.volNorm;
+    } else {
+      return this.volume;
+    }
+  };
+  /**
+   * Determines whether the results of Amplitude.process() will be
+   * Normalized. To normalize, Amplitude finds the difference the
+   * loudest reading it has processed and the maximum amplitude of
+   * 1.0. Amplitude adds this difference to all values to produce
+   * results that will reliably map between 0.0 and 1.0. However,
+   * if a louder moment occurs, the amount that Normalize adds to
+   * all the values will change. Accepts an optional boolean parameter
+   * (true or false). Normalizing is off by default.
+   *
+   * @method toggleNormalize
+   * @param {boolean} [boolean] set normalize to true (1) or false (0)
+   */
+  p5.Amplitude.prototype.toggleNormalize = function (bool) {
+    if (typeof bool === 'boolean') {
+      this.normalize = bool;
+    } else {
+      this.normalize = !this.normalize;
+    }
+  };
+  /**
+   *  Smooth Amplitude analysis by averaging with the last analysis 
+   *  frame. Off by default.
+   *
+   *  @method smooth
+   *  @param {Number} set smoothing from 0.0 <= 1
+   */
+  p5.Amplitude.prototype.smooth = function (s) {
+    if (s >= 0 && s < 1) {
+      this.smoothing = s;
+    } else {
+      console.log('Error: smoothing must be between 0 and 1');
+    }
+  };
+  p5.Amplitude.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this.input.disconnect();
+    this.output.disconnect();
+    this.input = this.processor = undefined;
+    this.output = undefined;
+  };
+}(master);
+var fft;
+fft = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+   *  <p>FFT (Fast Fourier Transform) is an analysis algorithm that
+   *  isolates individual
+   *  <a href="https://en.wikipedia.org/wiki/Audio_frequency">
+   *  audio frequencies</a> within a waveform.</p>
+   *
+   *  <p>Once instantiated, a p5.FFT object can return an array based on
+   *  two types of analyses: <br> • <code>FFT.waveform()</code> computes
+   *  amplitude values along the time domain. The array indices correspond
+   *  to samples across a brief moment in time. Each value represents
+   *  amplitude of the waveform at that sample of time.<br>
+   *  • <code>FFT.analyze() </code> computes amplitude values along the
+   *  frequency domain. The array indices correspond to frequencies (i.e.
+   *  pitches), from the lowest to the highest that humans can hear. Each
+   *  value represents amplitude at that slice of the frequency spectrum.
+   *  Use with <code>getEnergy()</code> to measure amplitude at specific
+   *  frequencies, or within a range of frequencies. </p>
+   *
+   *  <p>FFT analyzes a very short snapshot of sound called a sample
+   *  buffer. It returns an array of amplitude measurements, referred
+   *  to as <code>bins</code>. The array is 1024 bins long by default.
+   *  You can change the bin array length, but it must be a power of 2
+   *  between 16 and 1024 in order for the FFT algorithm to function
+   *  correctly. The actual size of the FFT buffer is twice the 
+   *  number of bins, so given a standard sample rate, the buffer is
+   *  2048/44100 seconds long.</p>
+   *  
+   * 
+   *  @class p5.FFT
+   *  @constructor
+   *  @param {Number} [smoothing]   Smooth results of Freq Spectrum.
+   *                                0.0 < smoothing < 1.0.
+   *                                Defaults to 0.8.
+   *  @param {Number} [bins]    Length of resulting array.
+   *                            Must be a power of two between
+   *                            16 and 1024. Defaults to 1024.
+   *  @return {Object}    FFT Object
+   *  @example
+   *  <div><code>
+   *  function preload(){
+   *    sound = loadSound('assets/Damscray_DancingTiger.mp3');
+   *  }
+   *  
+   *  function setup(){
+   *    var cnv = createCanvas(100,100);
+   *    cnv.mouseClicked(togglePlay);
+   *    fft = new p5.FFT();
+   *    sound.amp(0.2);
+   *  }
+   *  
+   *  function draw(){
+   *    background(0);
+   *  
+   *    var spectrum = fft.analyze(); 
+   *    noStroke();
+   *    fill(0,255,0); // spectrum is green
+   *    for (var i = 0; i< spectrum.length; i++){
+   *      var x = map(i, 0, spectrum.length, 0, width);
+   *      var h = -height + map(spectrum[i], 0, 255, height, 0);
+   *      rect(x, height, width / spectrum.length, h )
+   *    }
+   *  
+   *    var waveform = fft.waveform();
+   *    noFill();
+   *    beginShape();
+   *    stroke(255,0,0); // waveform is red
+   *    strokeWeight(1);
+   *    for (var i = 0; i< waveform.length; i++){
+   *      var x = map(i, 0, waveform.length, 0, width);
+   *      var y = map( waveform[i], -1, 1, 0, height);
+   *      vertex(x,y);
+   *    }
+   *    endShape();
+   *  
+   *    text('click to play/pause', 4, 10);
+   *  }
+   *  
+   *  // fade sound if mouse is over canvas
+   *  function togglePlay() {
+   *    if (sound.isPlaying()) {
+   *      sound.pause();
+   *    } else {
+   *      sound.loop();
+   *    }
+   *  }
+   *  </code></div>
+   */
+  p5.FFT = function (smoothing, bins) {
+    this.smoothing = smoothing || 0.8;
+    this.bins = bins || 1024;
+    var FFT_SIZE = bins * 2 || 2048;
+    this.input = this.analyser = p5sound.audiocontext.createAnalyser();
+    // default connections to p5sound fftMeter
+    p5sound.fftMeter.connect(this.analyser);
+    this.analyser.smoothingTimeConstant = this.smoothing;
+    this.analyser.fftSize = FFT_SIZE;
+    this.freqDomain = new Uint8Array(this.analyser.frequencyBinCount);
+    this.timeDomain = new Uint8Array(this.analyser.frequencyBinCount);
+    // predefined frequency ranages, these will be tweakable
+    this.bass = [
+      20,
+      140
+    ];
+    this.lowMid = [
+      140,
+      400
+    ];
+    this.mid = [
+      400,
+      2600
+    ];
+    this.highMid = [
+      2600,
+      5200
+    ];
+    this.treble = [
+      5200,
+      14000
+    ];
+    // add this p5.SoundFile to the soundArray
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Set the input source for the FFT analysis. If no source is
+   *  provided, FFT will analyze all sound in the sketch.
+   *
+   *  @method  setInput
+   *  @param {Object} [source] p5.sound object (or web audio API source node)
+   */
+  p5.FFT.prototype.setInput = function (source) {
+    if (!source) {
+      p5sound.fftMeter.connect(this.analyser);
+    } else {
+      if (source.output) {
+        source.output.connect(this.analyser);
+      } else if (source.connect) {
+        source.connect(this.analyser);
+      }
+      p5sound.fftMeter.disconnect();
+    }
+  };
+  /**
+   *  Returns an array of amplitude values (between -1.0 and +1.0) that represent
+   *  a snapshot of amplitude readings in a single buffer. Length will be
+   *  equal to bins (defaults to 1024). Can be used to draw the waveform
+   *  of a sound. 
+   *  
+   *  @method waveform
+   *  @param {Number} [bins]    Must be a power of two between
+   *                            16 and 1024. Defaults to 1024.
+   *  @param {String} [precision] If any value is provided, will return results
+   *                              in a Float32 Array which is more precise
+   *                              than a regular array.
+   *  @return {Array}  Array    Array of amplitude values (-1 to 1)
+   *                            over time. Array length = bins.
+   *
+   */
+  p5.FFT.prototype.waveform = function () {
+    var bins, mode, normalArray;
+    for (var i = 0; i < arguments.length; i++) {
+      if (typeof arguments[i] === 'number') {
+        bins = arguments[i];
+        this.analyser.fftSize = bins * 2;
+      }
+      if (typeof arguments[i] === 'string') {
+        mode = arguments[i];
+      }
+    }
+    // getFloatFrequencyData doesnt work in Safari as of 5/2015
+    if (mode && !p5.prototype._isSafari()) {
+      timeToFloat(this, this.timeDomain);
+      this.analyser.getFloatTimeDomainData(this.timeDomain);
+      return this.timeDomain;
+    } else {
+      timeToInt(this, this.timeDomain);
+      this.analyser.getByteTimeDomainData(this.timeDomain);
+      var normalArray = new Array();
+      for (var i = 0; i < this.timeDomain.length; i++) {
+        var scaled = p5.prototype.map(this.timeDomain[i], 0, 255, -1, 1);
+        normalArray.push(scaled);
+      }
+      return normalArray;
+    }
+  };
+  /**
+   *  Returns an array of amplitude values (between 0 and 255)
+   *  across the frequency spectrum. Length is equal to FFT bins
+   *  (1024 by default). The array indices correspond to frequencies
+   *  (i.e. pitches), from the lowest to the highest that humans can
+   *  hear. Each value represents amplitude at that slice of the
+   *  frequency spectrum. Must be called prior to using
+   *  <code>getEnergy()</code>.
+   *
+   *  @method analyze
+   *  @param {Number} [bins]    Must be a power of two between
+   *                             16 and 1024. Defaults to 1024.
+   *  @param {Number} [scale]    If "dB," returns decibel
+   *                             float measurements between
+   *                             -140 and 0 (max).
+   *                             Otherwise returns integers from 0-255.
+   *  @return {Array} spectrum    Array of energy (amplitude/volume)
+   *                              values across the frequency spectrum.
+   *                              Lowest energy (silence) = 0, highest
+   *                              possible is 255.
+   *  @example
+   *  <div><code>
+   *  var osc;
+   *  var fft;
+   *
+   *  function setup(){
+   *    createCanvas(100,100);
+   *    osc = new p5.Oscillator();
+   *    osc.amp(0);
+   *    osc.start();
+   *    fft = new p5.FFT();
+   *  }
+   *
+   *  function draw(){
+   *    background(0);
+   *
+   *    var freq = map(mouseX, 0, 800, 20, 15000);
+   *    freq = constrain(freq, 1, 20000);
+   *    osc.freq(freq);
+   *
+   *    var spectrum = fft.analyze(); 
+   *    noStroke();
+   *    fill(0,255,0); // spectrum is green
+   *    for (var i = 0; i< spectrum.length; i++){
+   *      var x = map(i, 0, spectrum.length, 0, width);
+   *      var h = -height + map(spectrum[i], 0, 255, height, 0);
+   *      rect(x, height, width / spectrum.length, h );
+   *    }
+   *
+   *    stroke(255);
+   *    text('Freq: ' + round(freq)+'Hz', 10, 10); 
+   *
+   *    isMouseOverCanvas();
+   *  }
+   *
+   *  // only play sound when mouse is over canvas
+   *  function isMouseOverCanvas() {
+   *    var mX = mouseX, mY = mouseY;
+   *    if (mX > 0 && mX < width && mY < height && mY > 0) {
+   *      osc.amp(0.5, 0.2);
+   *    } else {
+   *      osc.amp(0, 0.2);
+   *    }
+   *  }
+   *  </code></div>
+   *                                   
+   *
+   */
+  p5.FFT.prototype.analyze = function () {
+    var bins, mode;
+    for (var i = 0; i < arguments.length; i++) {
+      if (typeof arguments[i] === 'number') {
+        bins = this.bins = arguments[i];
+        this.analyser.fftSize = this.bins * 2;
+      }
+      if (typeof arguments[i] === 'string') {
+        mode = arguments[i];
+      }
+    }
+    if (mode && mode.toLowerCase() === 'db') {
+      freqToFloat(this);
+      this.analyser.getFloatFrequencyData(this.freqDomain);
+      return this.freqDomain;
+    } else {
+      freqToInt(this, this.freqDomain);
+      this.analyser.getByteFrequencyData(this.freqDomain);
+      var normalArray = Array.apply([], this.freqDomain);
+      normalArray.length === this.analyser.fftSize;
+      normalArray.constructor === Array;
+      return normalArray;
+    }
+  };
+  /**
+   *  Returns the amount of energy (volume) at a specific
+   *  <a href="en.wikipedia.org/wiki/Audio_frequency" target="_blank">
+   *  frequency</a>, or the average amount of energy between two
+   *  frequencies. Accepts Number(s) corresponding
+   *  to frequency (in Hz), or a String corresponding to predefined
+   *  frequency ranges ("bass", "lowMid", "mid", "highMid", "treble").
+   *  Returns a range between 0 (no energy/volume at that frequency) and
+   *  255 (maximum energy). 
+   *  <em>NOTE: analyze() must be called prior to getEnergy(). Analyze()
+   *  tells the FFT to analyze frequency data, and getEnergy() uses
+   *  the results determine the value at a specific frequency or
+   *  range of frequencies.</em></p>
+   *  
+   *  @method  getEnergy
+   *  @param  {Number|String} frequency1   Will return a value representing
+   *                                energy at this frequency. Alternately,
+   *                                the strings "bass", "lowMid" "mid",
+   *                                "highMid", and "treble" will return
+   *                                predefined frequency ranges.
+   *  @param  {Number} [frequency2] If a second frequency is given,
+   *                                will return average amount of
+   *                                energy that exists between the
+   *                                two frequencies.
+   *  @return {Number}   Energy   Energy (volume/amplitude) from
+   *                              0 and 255.
+   *                                       
+   */
+  p5.FFT.prototype.getEnergy = function (frequency1, frequency2) {
+    var nyquist = p5sound.audiocontext.sampleRate / 2;
+    if (frequency1 === 'bass') {
+      frequency1 = this.bass[0];
+      frequency2 = this.bass[1];
+    } else if (frequency1 === 'lowMid') {
+      frequency1 = this.lowMid[0];
+      frequency2 = this.lowMid[1];
+    } else if (frequency1 === 'mid') {
+      frequency1 = this.mid[0];
+      frequency2 = this.mid[1];
+    } else if (frequency1 === 'highMid') {
+      frequency1 = this.highMid[0];
+      frequency2 = this.highMid[1];
+    } else if (frequency1 === 'treble') {
+      frequency1 = this.treble[0];
+      frequency2 = this.treble[1];
+    }
+    if (typeof frequency1 !== 'number') {
+      throw 'invalid input for getEnergy()';
+    } else if (!frequency2) {
+      var index = Math.round(frequency1 / nyquist * this.freqDomain.length);
+      return this.freqDomain[index];
+    } else if (frequency1 && frequency2) {
+      // if second is higher than first
+      if (frequency1 > frequency2) {
+        var swap = frequency2;
+        frequency2 = frequency1;
+        frequency1 = swap;
+      }
+      var lowIndex = Math.round(frequency1 / nyquist * this.freqDomain.length);
+      var highIndex = Math.round(frequency2 / nyquist * this.freqDomain.length);
+      var total = 0;
+      var numFrequencies = 0;
+      // add up all of the values for the frequencies
+      for (var i = lowIndex; i <= highIndex; i++) {
+        total += this.freqDomain[i];
+        numFrequencies += 1;
+      }
+      // divide by total number of frequencies
+      var toReturn = total / numFrequencies;
+      return toReturn;
+    } else {
+      throw 'invalid input for getEnergy()';
+    }
+  };
+  // compatability with v.012, changed to getEnergy in v.0121. Will be deprecated...
+  p5.FFT.prototype.getFreq = function (freq1, freq2) {
+    console.log('getFreq() is deprecated. Please use getEnergy() instead.');
+    var x = this.getEnergy(freq1, freq2);
+    return x;
+  };
+  /**
+   *  Returns the 
+   *  <a href="http://en.wikipedia.org/wiki/Spectral_centroid" target="_blank">
+   *  spectral centroid</a> of the input signal.
+   *  <em>NOTE: analyze() must be called prior to getCentroid(). Analyze()
+   *  tells the FFT to analyze frequency data, and getCentroid() uses
+   *  the results determine the spectral centroid.</em></p>
+   *  
+   *  @method  getCentroid
+   *  @return {Number}   Spectral Centroid Frequency   Frequency of the spectral centroid in Hz.
+   * 
+   *
+   * @example
+   *  <div><code>
+   *
+   *
+   *function setup(){
+   *  cnv = createCanvas(800,400);
+   *  sound = new p5.AudioIn();
+   *  sound.start();
+   *  fft = new p5.FFT();
+   *  sound.connect(fft);
+   *}
+   *
+   *
+   *function draw(){
+   *
+   *  var centroidplot = 0.0;
+   *  var spectralCentroid = 0;
+   *  
+   *  
+   *  background(0);
+   *  stroke(0,255,0);
+   *  var spectrum = fft.analyze(); 
+   *  fill(0,255,0); // spectrum is green
+   *  
+   *  //draw the spectrum
+   *  
+   *  for (var i = 0; i< spectrum.length; i++){
+   *    var x = map(log(i), 0, log(spectrum.length), 0, width);
+   *    var h = map(spectrum[i], 0, 255, 0, height);
+   *    var rectangle_width = (log(i+1)-log(i))*(width/log(spectrum.length));
+   *    rect(x, height, rectangle_width, -h )
+   *  }
+  
+   *  var nyquist = 22050;
+   *  
+   *  // get the centroid
+   *  spectralCentroid = fft.getCentroid();
+   *  
+   *  // the mean_freq_index calculation is for the display.
+   *  var mean_freq_index = spectralCentroid/(nyquist/spectrum.length);
+   *
+   *  centroidplot = map(log(mean_freq_index), 0, log(spectrum.length), 0, width);
+   *
+   *
+   *  stroke(255,0,0); // the line showing where the centroid is will be red
+   *  
+   *  rect(centroidplot, 0, width / spectrum.length, height)
+   *  noStroke();
+   *  fill(255,255,255);  // text is white
+   *  textSize(40);
+   *  text("centroid: "+round(spectralCentroid)+" Hz", 10, 40);
+   *}
+   * </code></div>
+   */
+  p5.FFT.prototype.getCentroid = function () {
+    var nyquist = p5sound.audiocontext.sampleRate / 2;
+    var cumulative_sum = 0;
+    var centroid_normalization = 0;
+    for (var i = 0; i < this.freqDomain.length; i++) {
+      cumulative_sum += i * this.freqDomain[i];
+      centroid_normalization += this.freqDomain[i];
+    }
+    var mean_freq_index = 0;
+    if (centroid_normalization != 0) {
+      mean_freq_index = cumulative_sum / centroid_normalization;
+    }
+    var spec_centroid_freq = mean_freq_index * (nyquist / this.freqDomain.length);
+    return spec_centroid_freq;
+  };
+  /**
+   *  Smooth FFT analysis by averaging with the last analysis frame.
+   *  
+   *  @method smooth
+   *  @param {Number} smoothing    0.0 < smoothing < 1.0.
+   *                               Defaults to 0.8.
+   */
+  p5.FFT.prototype.smooth = function (s) {
+    if (s) {
+      this.smoothing = s;
+    }
+    this.analyser.smoothingTimeConstant = s;
+  };
+  p5.FFT.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this.analyser.disconnect();
+    this.analyser = undefined;
+  };
+  // helper methods to convert type from float (dB) to int (0-255)
+  var freqToFloat = function (fft) {
+    if (fft.freqDomain instanceof Float32Array === false) {
+      fft.freqDomain = new Float32Array(fft.analyser.frequencyBinCount);
+    }
+  };
+  var freqToInt = function (fft) {
+    if (fft.freqDomain instanceof Uint8Array === false) {
+      fft.freqDomain = new Uint8Array(fft.analyser.frequencyBinCount);
+    }
+  };
+  var timeToFloat = function (fft) {
+    if (fft.timeDomain instanceof Float32Array === false) {
+      fft.timeDomain = new Float32Array(fft.analyser.frequencyBinCount);
+    }
+  };
+  var timeToInt = function (fft) {
+    if (fft.timeDomain instanceof Uint8Array === false) {
+      fft.timeDomain = new Uint8Array(fft.analyser.frequencyBinCount);
+    }
+  };
+}(master);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_core_Tone;
+Tone_core_Tone = function () {
+  'use strict';
+  function isUndef(val) {
+    return val === void 0;
+  }
+  function isFunction(val) {
+    return typeof val === 'function';
+  }
+  var audioContext;
+  if (isUndef(window.AudioContext)) {
+    window.AudioContext = window.webkitAudioContext;
+  }
+  if (isUndef(window.OfflineAudioContext)) {
+    window.OfflineAudioContext = window.webkitOfflineAudioContext;
+  }
+  if (!isUndef(AudioContext)) {
+    audioContext = new AudioContext();
+  } else {
+    throw new Error('Web Audio is not supported in this browser');
+  }
+  if (!isFunction(AudioContext.prototype.createGain)) {
+    AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
+  }
+  if (!isFunction(AudioContext.prototype.createDelay)) {
+    AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
+  }
+  if (!isFunction(AudioContext.prototype.createPeriodicWave)) {
+    AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
+  }
+  if (!isFunction(AudioBufferSourceNode.prototype.start)) {
+    AudioBufferSourceNode.prototype.start = AudioBufferSourceNode.prototype.noteGrainOn;
+  }
+  if (!isFunction(AudioBufferSourceNode.prototype.stop)) {
+    AudioBufferSourceNode.prototype.stop = AudioBufferSourceNode.prototype.noteOff;
+  }
+  if (!isFunction(OscillatorNode.prototype.start)) {
+    OscillatorNode.prototype.start = OscillatorNode.prototype.noteOn;
+  }
+  if (!isFunction(OscillatorNode.prototype.stop)) {
+    OscillatorNode.prototype.stop = OscillatorNode.prototype.noteOff;
+  }
+  if (!isFunction(OscillatorNode.prototype.setPeriodicWave)) {
+    OscillatorNode.prototype.setPeriodicWave = OscillatorNode.prototype.setWaveTable;
+  }
+  AudioNode.prototype._nativeConnect = AudioNode.prototype.connect;
+  AudioNode.prototype.connect = function (B, outNum, inNum) {
+    if (B.input) {
+      if (Array.isArray(B.input)) {
+        if (isUndef(inNum)) {
+          inNum = 0;
+        }
+        this.connect(B.input[inNum]);
+      } else {
+        this.connect(B.input, outNum, inNum);
+      }
+    } else {
+      try {
+        if (B instanceof AudioNode) {
+          this._nativeConnect(B, outNum, inNum);
+        } else {
+          this._nativeConnect(B, outNum);
+        }
+      } catch (e) {
+        throw new Error('error connecting to node: ' + B);
+      }
+    }
+  };
+  var Tone = function (inputs, outputs) {
+    if (isUndef(inputs) || inputs === 1) {
+      this.input = this.context.createGain();
+    } else if (inputs > 1) {
+      this.input = new Array(inputs);
+    }
+    if (isUndef(outputs) || outputs === 1) {
+      this.output = this.context.createGain();
+    } else if (outputs > 1) {
+      this.output = new Array(inputs);
+    }
+  };
+  Tone.prototype.set = function (params, value, rampTime) {
+    if (this.isObject(params)) {
+      rampTime = value;
+    } else if (this.isString(params)) {
+      var tmpObj = {};
+      tmpObj[params] = value;
+      params = tmpObj;
+    }
+    for (var attr in params) {
+      value = params[attr];
+      var parent = this;
+      if (attr.indexOf('.') !== -1) {
+        var attrSplit = attr.split('.');
+        for (var i = 0; i < attrSplit.length - 1; i++) {
+          parent = parent[attrSplit[i]];
+        }
+        attr = attrSplit[attrSplit.length - 1];
+      }
+      var param = parent[attr];
+      if (isUndef(param)) {
+        continue;
+      }
+      if (Tone.Signal && param instanceof Tone.Signal || Tone.Param && param instanceof Tone.Param) {
+        if (param.value !== value) {
+          if (isUndef(rampTime)) {
+            param.value = value;
+          } else {
+            param.rampTo(value, rampTime);
+          }
+        }
+      } else if (param instanceof AudioParam) {
+        if (param.value !== value) {
+          param.value = value;
+        }
+      } else if (param instanceof Tone) {
+        param.set(value);
+      } else if (param !== value) {
+        parent[attr] = value;
+      }
+    }
+    return this;
+  };
+  Tone.prototype.get = function (params) {
+    if (isUndef(params)) {
+      params = this._collectDefaults(this.constructor);
+    } else if (this.isString(params)) {
+      params = [params];
+    }
+    var ret = {};
+    for (var i = 0; i < params.length; i++) {
+      var attr = params[i];
+      var parent = this;
+      var subRet = ret;
+      if (attr.indexOf('.') !== -1) {
+        var attrSplit = attr.split('.');
+        for (var j = 0; j < attrSplit.length - 1; j++) {
+          var subAttr = attrSplit[j];
+          subRet[subAttr] = subRet[subAttr] || {};
+          subRet = subRet[subAttr];
+          parent = parent[subAttr];
+        }
+        attr = attrSplit[attrSplit.length - 1];
+      }
+      var param = parent[attr];
+      if (this.isObject(params[attr])) {
+        subRet[attr] = param.get();
+      } else if (Tone.Signal && param instanceof Tone.Signal) {
+        subRet[attr] = param.value;
+      } else if (Tone.Param && param instanceof Tone.Param) {
+        subRet[attr] = param.value;
+      } else if (param instanceof AudioParam) {
+        subRet[attr] = param.value;
+      } else if (param instanceof Tone) {
+        subRet[attr] = param.get();
+      } else if (!isFunction(param) && !isUndef(param)) {
+        subRet[attr] = param;
+      }
+    }
+    return ret;
+  };
+  Tone.prototype._collectDefaults = function (constr) {
+    var ret = [];
+    if (!isUndef(constr.defaults)) {
+      ret = Object.keys(constr.defaults);
+    }
+    if (!isUndef(constr._super)) {
+      var superDefs = this._collectDefaults(constr._super);
+      for (var i = 0; i < superDefs.length; i++) {
+        if (ret.indexOf(superDefs[i]) === -1) {
+          ret.push(superDefs[i]);
+        }
+      }
+    }
+    return ret;
+  };
+  Tone.prototype.toString = function () {
+    for (var className in Tone) {
+      var isLetter = className[0].match(/^[A-Z]$/);
+      var sameConstructor = Tone[className] === this.constructor;
+      if (isFunction(Tone[className]) && isLetter && sameConstructor) {
+        return className;
+      }
+    }
+    return 'Tone';
+  };
+  Tone.context = audioContext;
+  Tone.prototype.context = Tone.context;
+  Tone.prototype.bufferSize = 2048;
+  Tone.prototype.blockTime = 128 / Tone.context.sampleRate;
+  Tone.prototype.dispose = function () {
+    if (!this.isUndef(this.input)) {
+      if (this.input instanceof AudioNode) {
+        this.input.disconnect();
+      }
+      this.input = null;
+    }
+    if (!this.isUndef(this.output)) {
+      if (this.output instanceof AudioNode) {
+        this.output.disconnect();
+      }
+      this.output = null;
+    }
+    return this;
+  };
+  var _silentNode = null;
+  Tone.prototype.noGC = function () {
+    this.output.connect(_silentNode);
+    return this;
+  };
+  AudioNode.prototype.noGC = function () {
+    this.connect(_silentNode);
+    return this;
+  };
+  Tone.prototype.connect = function (unit, outputNum, inputNum) {
+    if (Array.isArray(this.output)) {
+      outputNum = this.defaultArg(outputNum, 0);
+      this.output[outputNum].connect(unit, 0, inputNum);
+    } else {
+      this.output.connect(unit, outputNum, inputNum);
+    }
+    return this;
+  };
+  Tone.prototype.disconnect = function (outputNum) {
+    if (Array.isArray(this.output)) {
+      outputNum = this.defaultArg(outputNum, 0);
+      this.output[outputNum].disconnect();
+    } else {
+      this.output.disconnect();
+    }
+    return this;
+  };
+  Tone.prototype.connectSeries = function () {
+    if (arguments.length > 1) {
+      var currentUnit = arguments[0];
+      for (var i = 1; i < arguments.length; i++) {
+        var toUnit = arguments[i];
+        currentUnit.connect(toUnit);
+        currentUnit = toUnit;
+      }
+    }
+    return this;
+  };
+  Tone.prototype.connectParallel = function () {
+    var connectFrom = arguments[0];
+    if (arguments.length > 1) {
+      for (var i = 1; i < arguments.length; i++) {
+        var connectTo = arguments[i];
+        connectFrom.connect(connectTo);
+      }
+    }
+    return this;
+  };
+  Tone.prototype.chain = function () {
+    if (arguments.length > 0) {
+      var currentUnit = this;
+      for (var i = 0; i < arguments.length; i++) {
+        var toUnit = arguments[i];
+        currentUnit.connect(toUnit);
+        currentUnit = toUnit;
+      }
+    }
+    return this;
+  };
+  Tone.prototype.fan = function () {
+    if (arguments.length > 0) {
+      for (var i = 0; i < arguments.length; i++) {
+        this.connect(arguments[i]);
+      }
+    }
+    return this;
+  };
+  AudioNode.prototype.chain = Tone.prototype.chain;
+  AudioNode.prototype.fan = Tone.prototype.fan;
+  Tone.prototype.defaultArg = function (given, fallback) {
+    if (this.isObject(given) && this.isObject(fallback)) {
+      var ret = {};
+      for (var givenProp in given) {
+        ret[givenProp] = this.defaultArg(fallback[givenProp], given[givenProp]);
+      }
+      for (var fallbackProp in fallback) {
+        ret[fallbackProp] = this.defaultArg(given[fallbackProp], fallback[fallbackProp]);
+      }
+      return ret;
+    } else {
+      return isUndef(given) ? fallback : given;
+    }
+  };
+  Tone.prototype.optionsObject = function (values, keys, defaults) {
+    var options = {};
+    if (values.length === 1 && this.isObject(values[0])) {
+      options = values[0];
+    } else {
+      for (var i = 0; i < keys.length; i++) {
+        options[keys[i]] = values[i];
+      }
+    }
+    if (!this.isUndef(defaults)) {
+      return this.defaultArg(options, defaults);
+    } else {
+      return options;
+    }
+  };
+  Tone.prototype.isUndef = isUndef;
+  Tone.prototype.isFunction = isFunction;
+  Tone.prototype.isNumber = function (arg) {
+    return typeof arg === 'number';
+  };
+  Tone.prototype.isObject = function (arg) {
+    return Object.prototype.toString.call(arg) === '[object Object]' && arg.constructor === Object;
+  };
+  Tone.prototype.isBoolean = function (arg) {
+    return typeof arg === 'boolean';
+  };
+  Tone.prototype.isArray = function (arg) {
+    return Array.isArray(arg);
+  };
+  Tone.prototype.isString = function (arg) {
+    return typeof arg === 'string';
+  };
+  Tone.noOp = function () {
+  };
+  Tone.prototype._readOnly = function (property) {
+    if (Array.isArray(property)) {
+      for (var i = 0; i < property.length; i++) {
+        this._readOnly(property[i]);
+      }
+    } else {
+      Object.defineProperty(this, property, {
+        writable: false,
+        enumerable: true
+      });
+    }
+  };
+  Tone.prototype._writable = function (property) {
+    if (Array.isArray(property)) {
+      for (var i = 0; i < property.length; i++) {
+        this._writable(property[i]);
+      }
+    } else {
+      Object.defineProperty(this, property, { writable: true });
+    }
+  };
+  Tone.State = {
+    Started: 'started',
+    Stopped: 'stopped',
+    Paused: 'paused'
+  };
+  Tone.prototype.equalPowerScale = function (percent) {
+    var piFactor = 0.5 * Math.PI;
+    return Math.sin(percent * piFactor);
+  };
+  Tone.prototype.dbToGain = function (db) {
+    return Math.pow(2, db / 6);
+  };
+  Tone.prototype.gainToDb = function (gain) {
+    return 20 * (Math.log(gain) / Math.LN10);
+  };
+  Tone.prototype.now = function () {
+    return this.context.currentTime;
+  };
+  Tone.extend = function (child, parent) {
+    if (isUndef(parent)) {
+      parent = Tone;
+    }
+    function TempConstructor() {
+    }
+    TempConstructor.prototype = parent.prototype;
+    child.prototype = new TempConstructor();
+    child.prototype.constructor = child;
+    child._super = parent;
+  };
+  var newContextCallbacks = [];
+  Tone._initAudioContext = function (callback) {
+    callback(Tone.context);
+    newContextCallbacks.push(callback);
+  };
+  Tone.setContext = function (ctx) {
+    Tone.prototype.context = ctx;
+    Tone.context = ctx;
+    for (var i = 0; i < newContextCallbacks.length; i++) {
+      newContextCallbacks[i](ctx);
+    }
+  };
+  Tone.startMobile = function () {
+    var osc = Tone.context.createOscillator();
+    var silent = Tone.context.createGain();
+    silent.gain.value = 0;
+    osc.connect(silent);
+    silent.connect(Tone.context.destination);
+    var now = Tone.context.currentTime;
+    osc.start(now);
+    osc.stop(now + 1);
+  };
+  Tone._initAudioContext(function (audioContext) {
+    Tone.prototype.blockTime = 128 / audioContext.sampleRate;
+    _silentNode = audioContext.createGain();
+    _silentNode.gain.value = 0;
+    _silentNode.connect(audioContext.destination);
+  });
+  Tone.version = 'r7-dev';
+  return Tone;
+}();
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_signal_SignalBase;
+Tone_signal_SignalBase = function (Tone) {
+  'use strict';
+  Tone.SignalBase = function () {
+  };
+  Tone.extend(Tone.SignalBase);
+  Tone.SignalBase.prototype.connect = function (node, outputNumber, inputNumber) {
+    if (Tone.Signal && Tone.Signal === node.constructor || Tone.Param && Tone.Param === node.constructor || Tone.TimelineSignal && Tone.TimelineSignal === node.constructor) {
+      node._param.cancelScheduledValues(0);
+      node._param.value = 0;
+      node.overridden = true;
+    } else if (node instanceof AudioParam) {
+      node.cancelScheduledValues(0);
+      node.value = 0;
+    }
+    Tone.prototype.connect.call(this, node, outputNumber, inputNumber);
+    return this;
+  };
+  return Tone.SignalBase;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_signal_WaveShaper;
+Tone_signal_WaveShaper = function (Tone) {
+  'use strict';
+  Tone.WaveShaper = function (mapping, bufferLen) {
+    this._shaper = this.input = this.output = this.context.createWaveShaper();
+    this._curve = null;
+    if (Array.isArray(mapping)) {
+      this.curve = mapping;
+    } else if (isFinite(mapping) || this.isUndef(mapping)) {
+      this._curve = new Float32Array(this.defaultArg(mapping, 1024));
+    } else if (this.isFunction(mapping)) {
+      this._curve = new Float32Array(this.defaultArg(bufferLen, 1024));
+      this.setMap(mapping);
+    }
+  };
+  Tone.extend(Tone.WaveShaper, Tone.SignalBase);
+  Tone.WaveShaper.prototype.setMap = function (mapping) {
+    for (var i = 0, len = this._curve.length; i < len; i++) {
+      var normalized = i / len * 2 - 1;
+      this._curve[i] = mapping(normalized, i);
+    }
+    this._shaper.curve = this._curve;
+    return this;
+  };
+  Object.defineProperty(Tone.WaveShaper.prototype, 'curve', {
+    get: function () {
+      return this._shaper.curve;
+    },
+    set: function (mapping) {
+      this._curve = new Float32Array(mapping);
+      this._shaper.curve = this._curve;
+    }
+  });
+  Object.defineProperty(Tone.WaveShaper.prototype, 'oversample', {
+    get: function () {
+      return this._shaper.oversample;
+    },
+    set: function (oversampling) {
+      if ([
+          'none',
+          '2x',
+          '4x'
+        ].indexOf(oversampling) !== -1) {
+        this._shaper.oversample = oversampling;
+      } else {
+        throw new Error('invalid oversampling: ' + oversampling);
+      }
+    }
+  });
+  Tone.WaveShaper.prototype.dispose = function () {
+    Tone.prototype.dispose.call(this);
+    this._shaper.disconnect();
+    this._shaper = null;
+    this._curve = null;
+    return this;
+  };
+  return Tone.WaveShaper;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_core_Type;
+Tone_core_Type = function (Tone) {
+  'use strict';
+  Tone.Type = {
+    Default: 'number',
+    Time: 'time',
+    Frequency: 'frequency',
+    NormalRange: 'normalRange',
+    AudioRange: 'audioRange',
+    Decibels: 'db',
+    Interval: 'interval',
+    BPM: 'bpm',
+    Positive: 'positive',
+    Cents: 'cents',
+    Degrees: 'degrees',
+    MIDI: 'midi',
+    TransportTime: 'transportTime',
+    Ticks: 'tick',
+    Note: 'note',
+    Milliseconds: 'milliseconds',
+    Notation: 'notation'
+  };
+  Tone.prototype.isNowRelative = function () {
+    var nowRelative = new RegExp(/^\s*\+(.)+/i);
+    return function (note) {
+      return nowRelative.test(note);
+    };
+  }();
+  Tone.prototype.isTicks = function () {
+    var tickFormat = new RegExp(/^\d+i$/i);
+    return function (note) {
+      return tickFormat.test(note);
+    };
+  }();
+  Tone.prototype.isNotation = function () {
+    var notationFormat = new RegExp(/^[0-9]+[mnt]$/i);
+    return function (note) {
+      return notationFormat.test(note);
+    };
+  }();
+  Tone.prototype.isTransportTime = function () {
+    var transportTimeFormat = new RegExp(/^(\d+(\.\d+)?\:){1,2}(\d+(\.\d+)?)?$/i);
+    return function (transportTime) {
+      return transportTimeFormat.test(transportTime);
+    };
+  }();
+  Tone.prototype.isNote = function () {
+    var noteFormat = new RegExp(/^[a-g]{1}(b|#|x|bb)?-?[0-9]+$/i);
+    return function (note) {
+      return noteFormat.test(note);
+    };
+  }();
+  Tone.prototype.isFrequency = function () {
+    var freqFormat = new RegExp(/^\d*\.?\d+hz$/i);
+    return function (freq) {
+      return freqFormat.test(freq);
+    };
+  }();
+  function getTransportBpm() {
+    if (Tone.Transport && Tone.Transport.bpm) {
+      return Tone.Transport.bpm.value;
+    } else {
+      return 120;
+    }
+  }
+  function getTransportTimeSignature() {
+    if (Tone.Transport && Tone.Transport.timeSignature) {
+      return Tone.Transport.timeSignature;
+    } else {
+      return 4;
+    }
+  }
+  Tone.prototype.notationToSeconds = function (notation, bpm, timeSignature) {
+    bpm = this.defaultArg(bpm, getTransportBpm());
+    timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
+    var beatTime = 60 / bpm;
+    if (notation === '1n') {
+      notation = '1m';
+    }
+    var subdivision = parseInt(notation, 10);
+    var beats = 0;
+    if (subdivision === 0) {
+      beats = 0;
+    }
+    var lastLetter = notation.slice(-1);
+    if (lastLetter === 't') {
+      beats = 4 / subdivision * 2 / 3;
+    } else if (lastLetter === 'n') {
+      beats = 4 / subdivision;
+    } else if (lastLetter === 'm') {
+      beats = subdivision * timeSignature;
+    } else {
+      beats = 0;
+    }
+    return beatTime * beats;
+  };
+  Tone.prototype.transportTimeToSeconds = function (transportTime, bpm, timeSignature) {
+    bpm = this.defaultArg(bpm, getTransportBpm());
+    timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
+    var measures = 0;
+    var quarters = 0;
+    var sixteenths = 0;
+    var split = transportTime.split(':');
+    if (split.length === 2) {
+      measures = parseFloat(split[0]);
+      quarters = parseFloat(split[1]);
+    } else if (split.length === 1) {
+      quarters = parseFloat(split[0]);
+    } else if (split.length === 3) {
+      measures = parseFloat(split[0]);
+      quarters = parseFloat(split[1]);
+      sixteenths = parseFloat(split[2]);
+    }
+    var beats = measures * timeSignature + quarters + sixteenths / 4;
+    return beats * (60 / bpm);
+  };
+  Tone.prototype.ticksToSeconds = function (ticks, bpm) {
+    if (this.isUndef(Tone.Transport)) {
+      return 0;
+    }
+    ticks = parseFloat(ticks);
+    bpm = this.defaultArg(bpm, getTransportBpm());
+    var tickTime = 60 / bpm / Tone.Transport.PPQ;
+    return tickTime * ticks;
+  };
+  Tone.prototype.frequencyToSeconds = function (freq) {
+    return 1 / parseFloat(freq);
+  };
+  Tone.prototype.samplesToSeconds = function (samples) {
+    return samples / this.context.sampleRate;
+  };
+  Tone.prototype.secondsToSamples = function (seconds) {
+    return seconds * this.context.sampleRate;
+  };
+  Tone.prototype.secondsToTransportTime = function (seconds, bpm, timeSignature) {
+    bpm = this.defaultArg(bpm, getTransportBpm());
+    timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
+    var quarterTime = 60 / bpm;
+    var quarters = seconds / quarterTime;
+    var measures = Math.floor(quarters / timeSignature);
+    var sixteenths = quarters % 1 * 4;
+    quarters = Math.floor(quarters) % timeSignature;
+    var progress = [
+      measures,
+      quarters,
+      sixteenths
+    ];
+    return progress.join(':');
+  };
+  Tone.prototype.secondsToFrequency = function (seconds) {
+    return 1 / seconds;
+  };
+  Tone.prototype.toTransportTime = function (time, bpm, timeSignature) {
+    var seconds = this.toSeconds(time);
+    return this.secondsToTransportTime(seconds, bpm, timeSignature);
+  };
+  Tone.prototype.toFrequency = function (freq, now) {
+    if (this.isFrequency(freq)) {
+      return parseFloat(freq);
+    } else if (this.isNotation(freq) || this.isTransportTime(freq)) {
+      return this.secondsToFrequency(this.toSeconds(freq, now));
+    } else if (this.isNote(freq)) {
+      return this.noteToFrequency(freq);
+    } else {
+      return freq;
+    }
+  };
+  Tone.prototype.toTicks = function (time) {
+    if (this.isUndef(Tone.Transport)) {
+      return 0;
+    }
+    var bpm = Tone.Transport.bpm.value;
+    var plusNow = 0;
+    if (this.isNowRelative(time)) {
+      time = time.replace('+', '');
+      plusNow = Tone.Transport.ticks;
+    } else if (this.isUndef(time)) {
+      return Tone.Transport.ticks;
+    }
+    var seconds = this.toSeconds(time);
+    var quarter = 60 / bpm;
+    var quarters = seconds / quarter;
+    var tickNum = quarters * Tone.Transport.PPQ;
+    return Math.round(tickNum + plusNow);
+  };
+  Tone.prototype.toSamples = function (time) {
+    var seconds = this.toSeconds(time);
+    return Math.round(seconds * this.context.sampleRate);
+  };
+  Tone.prototype.toSeconds = function (time, now) {
+    now = this.defaultArg(now, this.now());
+    if (this.isNumber(time)) {
+      return time;
+    } else if (this.isString(time)) {
+      var plusTime = 0;
+      if (this.isNowRelative(time)) {
+        time = time.replace('+', '');
+        plusTime = now;
+      }
+      var betweenParens = time.match(/\(([^)(]+)\)/g);
+      if (betweenParens) {
+        for (var j = 0; j < betweenParens.length; j++) {
+          var symbol = betweenParens[j].replace(/[\(\)]/g, '');
+          var symbolVal = this.toSeconds(symbol);
+          time = time.replace(betweenParens[j], symbolVal);
+        }
+      }
+      if (time.indexOf('@') !== -1) {
+        var quantizationSplit = time.split('@');
+        if (!this.isUndef(Tone.Transport)) {
+          var toQuantize = quantizationSplit[0].trim();
+          if (toQuantize === '') {
+            toQuantize = undefined;
+          }
+          if (plusTime > 0) {
+            toQuantize = '+' + toQuantize;
+            plusTime = 0;
+          }
+          var subdivision = quantizationSplit[1].trim();
+          time = Tone.Transport.quantize(toQuantize, subdivision);
+        } else {
+          throw new Error('quantization requires Tone.Transport');
+        }
+      } else {
+        var components = time.split(/[\(\)\-\+\/\*]/);
+        if (components.length > 1) {
+          var originalTime = time;
+          for (var i = 0; i < components.length; i++) {
+            var symb = components[i].trim();
+            if (symb !== '') {
+              var val = this.toSeconds(symb);
+              time = time.replace(symb, val);
+            }
+          }
+          try {
+            time = eval(time);
+          } catch (e) {
+            throw new EvalError('cannot evaluate Time: ' + originalTime);
+          }
+        } else if (this.isNotation(time)) {
+          time = this.notationToSeconds(time);
+        } else if (this.isTransportTime(time)) {
+          time = this.transportTimeToSeconds(time);
+        } else if (this.isFrequency(time)) {
+          time = this.frequencyToSeconds(time);
+        } else if (this.isTicks(time)) {
+          time = this.ticksToSeconds(time);
+        } else {
+          time = parseFloat(time);
+        }
+      }
+      return time + plusTime;
+    } else {
+      return now;
+    }
+  };
+  Tone.prototype.toNotation = function (time, bpm, timeSignature) {
+    var testNotations = [
+      '1m',
+      '2n',
+      '4n',
+      '8n',
+      '16n',
+      '32n',
+      '64n',
+      '128n'
+    ];
+    var retNotation = toNotationHelper.call(this, time, bpm, timeSignature, testNotations);
+    var testTripletNotations = [
+      '1m',
+      '2n',
+      '2t',
+      '4n',
+      '4t',
+      '8n',
+      '8t',
+      '16n',
+      '16t',
+      '32n',
+      '32t',
+      '64n',
+      '64t',
+      '128n'
+    ];
+    var retTripletNotation = toNotationHelper.call(this, time, bpm, timeSignature, testTripletNotations);
+    if (retTripletNotation.split('+').length < retNotation.split('+').length) {
+      return retTripletNotation;
+    } else {
+      return retNotation;
+    }
+  };
+  function toNotationHelper(time, bpm, timeSignature, testNotations) {
+    var seconds = this.toSeconds(time);
+    var threshold = this.notationToSeconds(testNotations[testNotations.length - 1], bpm, timeSignature);
+    var retNotation = '';
+    for (var i = 0; i < testNotations.length; i++) {
+      var notationTime = this.notationToSeconds(testNotations[i], bpm, timeSignature);
+      var multiple = seconds / notationTime;
+      var floatingPointError = 0.000001;
+      if (1 - multiple % 1 < floatingPointError) {
+        multiple += floatingPointError;
+      }
+      multiple = Math.floor(multiple);
+      if (multiple > 0) {
+        if (multiple === 1) {
+          retNotation += testNotations[i];
+        } else {
+          retNotation += multiple.toString() + '*' + testNotations[i];
+        }
+        seconds -= multiple * notationTime;
+        if (seconds < threshold) {
+          break;
+        } else {
+          retNotation += ' + ';
+        }
+      }
+    }
+    if (retNotation === '') {
+      retNotation = '0';
+    }
+    return retNotation;
+  }
+  Tone.prototype.fromUnits = function (val, units) {
+    if (this.convert || this.isUndef(this.convert)) {
+      switch (units) {
+      case Tone.Type.Time:
+        return this.toSeconds(val);
+      case Tone.Type.Frequency:
+        return this.toFrequency(val);
+      case Tone.Type.Decibels:
+        return this.dbToGain(val);
+      case Tone.Type.NormalRange:
+        return Math.min(Math.max(val, 0), 1);
+      case Tone.Type.AudioRange:
+        return Math.min(Math.max(val, -1), 1);
+      case Tone.Type.Positive:
+        return Math.max(val, 0);
+      default:
+        return val;
+      }
+    } else {
+      return val;
+    }
+  };
+  Tone.prototype.toUnits = function (val, units) {
+    if (this.convert || this.isUndef(this.convert)) {
+      switch (units) {
+      case Tone.Type.Decibels:
+        return this.gainToDb(val);
+      default:
+        return val;
+      }
+    } else {
+      return val;
+    }
+  };
+  var noteToScaleIndex = {
+    'cbb': -2,
+    'cb': -1,
+    'c': 0,
+    'c#': 1,
+    'cx': 2,
+    'dbb': 0,
+    'db': 1,
+    'd': 2,
+    'd#': 3,
+    'dx': 4,
+    'ebb': 2,
+    'eb': 3,
+    'e': 4,
+    'e#': 5,
+    'ex': 6,
+    'fbb': 3,
+    'fb': 4,
+    'f': 5,
+    'f#': 6,
+    'fx': 7,
+    'gbb': 5,
+    'gb': 6,
+    'g': 7,
+    'g#': 8,
+    'gx': 9,
+    'abb': 7,
+    'ab': 8,
+    'a': 9,
+    'a#': 10,
+    'ax': 11,
+    'bbb': 9,
+    'bb': 10,
+    'b': 11,
+    'b#': 12,
+    'bx': 13
+  };
+  var scaleIndexToNote = [
+    'C',
+    'C#',
+    'D',
+    'D#',
+    'E',
+    'F',
+    'F#',
+    'G',
+    'G#',
+    'A',
+    'A#',
+    'B'
+  ];
+  Tone.A4 = 440;
+  Tone.prototype.noteToFrequency = function (note) {
+    var parts = note.split(/(-?\d+)/);
+    if (parts.length === 3) {
+      var index = noteToScaleIndex[parts[0].toLowerCase()];
+      var octave = parts[1];
+      var noteNumber = index + (parseInt(octave, 10) + 1) * 12;
+      return this.midiToFrequency(noteNumber);
+    } else {
+      return 0;
+    }
+  };
+  Tone.prototype.frequencyToNote = function (freq) {
+    var log = Math.log(freq / Tone.A4) / Math.LN2;
+    var noteNumber = Math.round(12 * log) + 57;
+    var octave = Math.floor(noteNumber / 12);
+    if (octave < 0) {
+      noteNumber += -12 * octave;
+    }
+    var noteName = scaleIndexToNote[noteNumber % 12];
+    return noteName + octave.toString();
+  };
+  Tone.prototype.intervalToFrequencyRatio = function (interval) {
+    return Math.pow(2, interval / 12);
+  };
+  Tone.prototype.midiToNote = function (midiNumber) {
+    var octave = Math.floor(midiNumber / 12) - 1;
+    var note = midiNumber % 12;
+    return scaleIndexToNote[note] + octave;
+  };
+  Tone.prototype.noteToMidi = function (note) {
+    var parts = note.split(/(\d+)/);
+    if (parts.length === 3) {
+      var index = noteToScaleIndex[parts[0].toLowerCase()];
+      var octave = parts[1];
+      return index + (parseInt(octave, 10) + 1) * 12;
+    } else {
+      return 0;
+    }
+  };
+  Tone.prototype.midiToFrequency = function (midi) {
+    return Tone.A4 * Math.pow(2, (midi - 69) / 12);
+  };
+  return Tone;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_core_Param;
+Tone_core_Param = function (Tone) {
+  'use strict';
+  Tone.Param = function () {
+    var options = this.optionsObject(arguments, [
+      'param',
+      'units',
+      'convert'
+    ], Tone.Param.defaults);
+    this._param = this.input = options.param;
+    this.units = options.units;
+    this.convert = options.convert;
+    this.overridden = false;
+    if (!this.isUndef(options.value)) {
+      this.value = options.value;
+    }
+  };
+  Tone.extend(Tone.Param);
+  Tone.Param.defaults = {
+    'units': Tone.Type.Default,
+    'convert': true,
+    'param': undefined
+  };
+  Object.defineProperty(Tone.Param.prototype, 'value', {
+    get: function () {
+      return this._toUnits(this._param.value);
+    },
+    set: function (value) {
+      var convertedVal = this._fromUnits(value);
+      this._param.value = convertedVal;
+    }
+  });
+  Tone.Param.prototype._fromUnits = function (val) {
+    if (this.convert || this.isUndef(this.convert)) {
+      switch (this.units) {
+      case Tone.Type.Time:
+        return this.toSeconds(val);
+      case Tone.Type.Frequency:
+        return this.toFrequency(val);
+      case Tone.Type.Decibels:
+        return this.dbToGain(val);
+      case Tone.Type.NormalRange:
+        return Math.min(Math.max(val, 0), 1);
+      case Tone.Type.AudioRange:
+        return Math.min(Math.max(val, -1), 1);
+      case Tone.Type.Positive:
+        return Math.max(val, 0);
+      default:
+        return val;
+      }
+    } else {
+      return val;
+    }
+  };
+  Tone.Param.prototype._toUnits = function (val) {
+    if (this.convert || this.isUndef(this.convert)) {
+      switch (this.units) {
+      case Tone.Type.Decibels:
+        return this.gainToDb(val);
+      default:
+        return val;
+      }
+    } else {
+      return val;
+    }
+  };
+  Tone.Param.prototype._minOutput = 0.00001;
+  Tone.Param.prototype.setValueAtTime = function (value, time) {
+    value = this._fromUnits(value);
+    this._param.setValueAtTime(value, this.toSeconds(time));
+    return this;
+  };
+  Tone.Param.prototype.setRampPoint = function (now) {
+    now = this.defaultArg(now, this.now());
+    var currentVal = this._param.value;
+    this._param.setValueAtTime(currentVal, now);
+    return this;
+  };
+  Tone.Param.prototype.linearRampToValueAtTime = function (value, endTime) {
+    value = this._fromUnits(value);
+    this._param.linearRampToValueAtTime(value, this.toSeconds(endTime));
+    return this;
+  };
+  Tone.Param.prototype.exponentialRampToValueAtTime = function (value, endTime) {
+    value = this._fromUnits(value);
+    value = Math.max(this._minOutput, value);
+    this._param.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
+    return this;
+  };
+  Tone.Param.prototype.exponentialRampToValue = function (value, rampTime) {
+    var now = this.now();
+    var currentVal = this.value;
+    this.setValueAtTime(Math.max(currentVal, this._minOutput), now);
+    this.exponentialRampToValueAtTime(value, now + this.toSeconds(rampTime));
+    return this;
+  };
+  Tone.Param.prototype.linearRampToValue = function (value, rampTime) {
+    var now = this.now();
+    this.setRampPoint(now);
+    this.linearRampToValueAtTime(value, now + this.toSeconds(rampTime));
+    return this;
+  };
+  Tone.Param.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
+    value = this._fromUnits(value);
+    value = Math.max(this._minOutput, value);
+    timeConstant = Math.max(this._minOutput, timeConstant);
+    this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
+    return this;
+  };
+  Tone.Param.prototype.setValueCurveAtTime = function (values, startTime, duration) {
+    for (var i = 0; i < values.length; i++) {
+      values[i] = this._fromUnits(values[i]);
+    }
+    this._param.setValueCurveAtTime(values, this.toSeconds(startTime), this.toSeconds(duration));
+    return this;
+  };
+  Tone.Param.prototype.cancelScheduledValues = function (startTime) {
+    this._param.cancelScheduledValues(this.toSeconds(startTime));
+    return this;
+  };
+  Tone.Param.prototype.rampTo = function (value, rampTime) {
+    rampTime = this.defaultArg(rampTime, 0);
+    if (this.units === Tone.Type.Frequency || this.units === Tone.Type.BPM) {
+      this.exponentialRampToValue(value, rampTime);
+    } else {
+      this.linearRampToValue(value, rampTime);
+    }
+    return this;
+  };
+  Tone.Param.prototype.dispose = function () {
+    Tone.prototype.dispose.call(this);
+    this._param = null;
+    return this;
+  };
+  return Tone.Param;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_core_Gain;
+Tone_core_Gain = function (Tone) {
+  'use strict';
+  Tone.Gain = function () {
+    var options = this.optionsObject(arguments, [
+      'gain',
+      'units'
+    ], Tone.Gain.defaults);
+    this.input = this.output = this._gainNode = this.context.createGain();
+    this.gain = new Tone.Param({
+      'param': this._gainNode.gain,
+      'units': options.units,
+      'value': options.gain,
+      'convert': options.convert
+    });
+    this._readOnly('gain');
+  };
+  Tone.extend(Tone.Gain);
+  Tone.Gain.defaults = {
+    'gain': 1,
+    'convert': true
+  };
+  Tone.Gain.prototype.dispose = function () {
+    Tone.Param.prototype.dispose.call(this);
+    this._gainNode.disconnect();
+    this._gainNode = null;
+    this._writable('gain');
+    this.gain.dispose();
+    this.gain = null;
+  };
+  return Tone.Gain;
+}(Tone_core_Tone, Tone_core_Param);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_signal_Signal;
+Tone_signal_Signal = function (Tone) {
+  'use strict';
+  Tone.Signal = function () {
+    var options = this.optionsObject(arguments, [
+      'value',
+      'units'
+    ], Tone.Signal.defaults);
+    this.output = this._gain = this.context.createGain();
+    options.param = this._gain.gain;
+    Tone.Param.call(this, options);
+    this.input = this._param = this._gain.gain;
+    Tone.Signal._constant.chain(this._gain);
+  };
+  Tone.extend(Tone.Signal, Tone.Param);
+  Tone.Signal.defaults = {
+    'value': 0,
+    'units': Tone.Type.Default,
+    'convert': true
+  };
+  Tone.Signal.prototype.connect = Tone.SignalBase.prototype.connect;
+  Tone.Signal.prototype.dispose = function () {
+    Tone.Param.prototype.dispose.call(this);
+    this._param = null;
+    this._gain.disconnect();
+    this._gain = null;
+    return this;
+  };
+  Tone.Signal._constant = null;
+  Tone._initAudioContext(function (audioContext) {
+    var buffer = audioContext.createBuffer(1, 128, audioContext.sampleRate);
+    var arr = buffer.getChannelData(0);
+    for (var i = 0; i < arr.length; i++) {
+      arr[i] = 1;
+    }
+    Tone.Signal._constant = audioContext.createBufferSource();
+    Tone.Signal._constant.channelCount = 1;
+    Tone.Signal._constant.channelCountMode = 'explicit';
+    Tone.Signal._constant.buffer = buffer;
+    Tone.Signal._constant.loop = true;
+    Tone.Signal._constant.start(0);
+    Tone.Signal._constant.noGC();
+  });
+  return Tone.Signal;
+}(Tone_core_Tone, Tone_signal_WaveShaper, Tone_core_Type, Tone_core_Param);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_signal_Add;
+Tone_signal_Add = function (Tone) {
+  'use strict';
+  Tone.Add = function (value) {
+    Tone.call(this, 2, 0);
+    this._sum = this.input[0] = this.input[1] = this.output = this.context.createGain();
+    this._param = this.input[1] = new Tone.Signal(value);
+    this._param.connect(this._sum);
+  };
+  Tone.extend(Tone.Add, Tone.Signal);
+  Tone.Add.prototype.dispose = function () {
+    Tone.prototype.dispose.call(this);
+    this._sum.disconnect();
+    this._sum = null;
+    this._param.dispose();
+    this._param = null;
+    return this;
+  };
+  return Tone.Add;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_signal_Multiply;
+Tone_signal_Multiply = function (Tone) {
+  'use strict';
+  Tone.Multiply = function (value) {
+    Tone.call(this, 2, 0);
+    this._mult = this.input[0] = this.output = this.context.createGain();
+    this._param = this.input[1] = this.output.gain;
+    this._param.value = this.defaultArg(value, 0);
+  };
+  Tone.extend(Tone.Multiply, Tone.Signal);
+  Tone.Multiply.prototype.dispose = function () {
+    Tone.prototype.dispose.call(this);
+    this._mult.disconnect();
+    this._mult = null;
+    this._param = null;
+    return this;
+  };
+  return Tone.Multiply;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_signal_Scale;
+Tone_signal_Scale = function (Tone) {
+  'use strict';
+  Tone.Scale = function (outputMin, outputMax) {
+    this._outputMin = this.defaultArg(outputMin, 0);
+    this._outputMax = this.defaultArg(outputMax, 1);
+    this._scale = this.input = new Tone.Multiply(1);
+    this._add = this.output = new Tone.Add(0);
+    this._scale.connect(this._add);
+    this._setRange();
+  };
+  Tone.extend(Tone.Scale, Tone.SignalBase);
+  Object.defineProperty(Tone.Scale.prototype, 'min', {
+    get: function () {
+      return this._outputMin;
+    },
+    set: function (min) {
+      this._outputMin = min;
+      this._setRange();
+    }
+  });
+  Object.defineProperty(Tone.Scale.prototype, 'max', {
+    get: function () {
+      return this._outputMax;
+    },
+    set: function (max) {
+      this._outputMax = max;
+      this._setRange();
+    }
+  });
+  Tone.Scale.prototype._setRange = function () {
+    this._add.value = this._outputMin;
+    this._scale.value = this._outputMax - this._outputMin;
+  };
+  Tone.Scale.prototype.dispose = function () {
+    Tone.prototype.dispose.call(this);
+    this._add.dispose();
+    this._add = null;
+    this._scale.dispose();
+    this._scale = null;
+    return this;
+  };
+  return Tone.Scale;
+}(Tone_core_Tone, Tone_signal_Add, Tone_signal_Multiply);
+var signal;
+signal = function () {
+  'use strict';
+  // Signal is built with the Tone.js signal by Yotam Mann
+  // https://github.com/TONEnoTONE/Tone.js/
+  var Signal = Tone_signal_Signal;
+  var Add = Tone_signal_Add;
+  var Mult = Tone_signal_Multiply;
+  var Scale = Tone_signal_Scale;
+  var Tone = Tone_core_Tone;
+  var p5sound = master;
+  Tone.setContext(p5sound.audiocontext);
+  /**
+   *  <p>p5.Signal is a constant audio-rate signal used by p5.Oscillator
+   *  and p5.Envelope for modulation math.</p>
+   *
+   *  <p>This is necessary because Web Audio is processed on a seprate clock.
+   *  For example, the p5 draw loop runs about 60 times per second. But
+   *  the audio clock must process samples 44100 times per second. If we
+   *  want to add a value to each of those samples, we can't do it in the
+   *  draw loop, but we can do it by adding a constant-rate audio signal.</p.
+   *  
+   *  <p>This class mostly functions behind the scenes in p5.sound, and returns
+   *  a Tone.Signal from the Tone.js library by Yotam Mann.
+   *  If you want to work directly with audio signals for modular
+   *  synthesis, check out
+   *  <a href='http://bit.ly/1oIoEng' target=_'blank'>tone.js.</a></p>
+   *
+   *  @class  p5.Signal
+   *  @constructor
+   *  @return {Tone.Signal} A Signal object from the Tone.js library
+   *  @example
+   *  <div><code>
+   *  function setup() {
+   *    carrier = new p5.Oscillator('sine');
+   *    carrier.amp(1); // set amplitude
+   *    carrier.freq(220); // set frequency
+   *    carrier.start(); // start oscillating
+   *    
+   *    modulator = new p5.Oscillator('sawtooth');
+   *    modulator.disconnect();
+   *    modulator.amp(1);
+   *    modulator.freq(4);
+   *    modulator.start();
+   *
+   *    // Modulator's default amplitude range is -1 to 1.
+   *    // Multiply it by -200, so the range is -200 to 200
+   *    // then add 220 so the range is 20 to 420
+   *    carrier.freq( modulator.mult(-200).add(220) );
+   *  }
+   *  </code></div>
+   */
+  p5.Signal = function (value) {
+    var s = new Signal(value);
+    // p5sound.soundArray.push(s);
+    return s;
+  };
+  /**
+   *  Fade to value, for smooth transitions
+   *
+   *  @method  fade
+   *  @param  {Number} value          Value to set this signal
+   *  @param  {[Number]} secondsFromNow Length of fade, in seconds from now
+   */
+  Signal.prototype.fade = Signal.prototype.linearRampToValueAtTime;
+  Mult.prototype.fade = Signal.prototype.fade;
+  Add.prototype.fade = Signal.prototype.fade;
+  Scale.prototype.fade = Signal.prototype.fade;
+  /**
+   *  Connect a p5.sound object or Web Audio node to this
+   *  p5.Signal so that its amplitude values can be scaled.
+   *  
+   *  @param {Object} input
+   */
+  Signal.prototype.setInput = function (_input) {
+    _input.connect(this);
+  };
+  Mult.prototype.setInput = Signal.prototype.setInput;
+  Add.prototype.setInput = Signal.prototype.setInput;
+  Scale.prototype.setInput = Signal.prototype.setInput;
+  // signals can add / mult / scale themselves
+  /**
+   *  Add a constant value to this audio signal,
+   *  and return the resulting audio signal. Does
+   *  not change the value of the original signal,
+   *  instead it returns a new p5.SignalAdd.
+   *  
+   *  @method  add
+   *  @param {Number} number
+   *  @return {p5.SignalAdd} object
+   */
+  Signal.prototype.add = function (num) {
+    var add = new Add(num);
+    // add.setInput(this);
+    this.connect(add);
+    return add;
+  };
+  Mult.prototype.add = Signal.prototype.add;
+  Add.prototype.add = Signal.prototype.add;
+  Scale.prototype.add = Signal.prototype.add;
+  /**
+   *  Multiply this signal by a constant value,
+   *  and return the resulting audio signal. Does
+   *  not change the value of the original signal,
+   *  instead it returns a new p5.SignalMult.
+   *  
+   *  @method  mult
+   *  @param {Number} number to multiply
+   *  @return {Tone.Multiply} object
+   */
+  Signal.prototype.mult = function (num) {
+    var mult = new Mult(num);
+    // mult.setInput(this);
+    this.connect(mult);
+    return mult;
+  };
+  Mult.prototype.mult = Signal.prototype.mult;
+  Add.prototype.mult = Signal.prototype.mult;
+  Scale.prototype.mult = Signal.prototype.mult;
+  /**
+   *  Scale this signal value to a given range,
+   *  and return the result as an audio signal. Does
+   *  not change the value of the original signal,
+   *  instead it returns a new p5.SignalScale.
+   *  
+   *  @method  scale
+   *  @param {Number} number to multiply
+   *  @param  {Number} inMin  input range minumum
+   *  @param  {Number} inMax  input range maximum
+   *  @param  {Number} outMin input range minumum
+   *  @param  {Number} outMax input range maximum
+   *  @return {p5.SignalScale} object
+   */
+  Signal.prototype.scale = function (inMin, inMax, outMin, outMax) {
+    var mapOutMin, mapOutMax;
+    if (arguments.length === 4) {
+      mapOutMin = p5.prototype.map(outMin, inMin, inMax, 0, 1) - 0.5;
+      mapOutMax = p5.prototype.map(outMax, inMin, inMax, 0, 1) - 0.5;
+    } else {
+      mapOutMin = arguments[0];
+      mapOutMax = arguments[1];
+    }
+    var scale = new Scale(mapOutMin, mapOutMax);
+    this.connect(scale);
+    return scale;
+  };
+  Mult.prototype.scale = Signal.prototype.scale;
+  Add.prototype.scale = Signal.prototype.scale;
+  Scale.prototype.scale = Signal.prototype.scale;
+}(Tone_signal_Signal, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_core_Tone, master);
+var oscillator;
+oscillator = function () {
+  'use strict';
+  var p5sound = master;
+  var Signal = Tone_signal_Signal;
+  var Add = Tone_signal_Add;
+  var Mult = Tone_signal_Multiply;
+  var Scale = Tone_signal_Scale;
+  /**
+   *  <p>Creates a signal that oscillates between -1.0 and 1.0.
+   *  By default, the oscillation takes the form of a sinusoidal
+   *  shape ('sine'). Additional types include 'triangle',
+   *  'sawtooth' and 'square'. The frequency defaults to
+   *  440 oscillations per second (440Hz, equal to the pitch of an
+   *  'A' note).</p> 
+   *
+   *  <p>Set the type of oscillation with setType(), or by creating a
+   *  specific oscillator.</p> For example:
+   *  <code>new p5.SinOsc(freq)</code>
+   *  <code>new p5.TriOsc(freq)</code>
+   *  <code>new p5.SqrOsc(freq)</code>
+   *  <code>new p5.SawOsc(freq)</code>.
+   *  </p>
+   *  
+   *  @class p5.Oscillator
+   *  @constructor
+   *  @param {Number} [freq] frequency defaults to 440Hz
+   *  @param {String} [type] type of oscillator. Options:
+   *                         'sine' (default), 'triangle',
+   *                         'sawtooth', 'square'
+   *  @return {Object}    Oscillator object
+   *  @example
+   *  <div><code>
+   *  var osc;
+   *  var playing = false;
+   *  
+   *  function setup() {
+   *    backgroundColor = color(255,0,255);
+   *    textAlign(CENTER);
+   *    
+   *    osc = new p5.Oscillator();
+   *    osc.setType('sine');
+   *    osc.freq(240);
+   *    osc.amp(0);
+   *    osc.start();
+   *  }
+   *
+   *  function draw() {
+   *    background(backgroundColor)
+   *    text('click to play', width/2, height/2);
+   *  }
+   *
+   *  function mouseClicked() {
+   *    if (mouseX > 0 && mouseX < width && mouseY < height && mouseY > 0) {
+   *      if (!playing) {
+   *        // ramp amplitude to 0.5 over 0.1 seconds
+   *        osc.amp(0.5, 0.05);
+   *        playing = true;
+   *        backgroundColor = color(0,255,255);
+   *      } else {
+   *        // ramp amplitude to 0 over 0.5 seconds
+   *        osc.amp(0, 0.5);
+   *        playing = false;
+   *        backgroundColor = color(255,0,255);
+   *      }
+   *    }
+   *  }
+   *  </code> </div>
+   */
+  p5.Oscillator = function (freq, type) {
+    if (typeof freq === 'string') {
+      var f = type;
+      type = freq;
+      freq = f;
+    }
+    if (typeof type === 'number') {
+      var f = type;
+      type = freq;
+      freq = f;
+    }
+    this.started = false;
+    // components
+    this.phaseAmount = undefined;
+    this.oscillator = p5sound.audiocontext.createOscillator();
+    this.f = freq || 440;
+    // frequency
+    this.oscillator.frequency.setValueAtTime(this.f, p5sound.audiocontext.currentTime);
+    this.oscillator.type = type || 'sine';
+    var o = this.oscillator;
+    // connections
+    this.input = p5sound.audiocontext.createGain();
+    this.output = p5sound.audiocontext.createGain();
+    this._freqMods = [];
+    // modulators connected to this oscillator's frequency
+    // set default output gain to 0.5
+    this.output.gain.value = 0.5;
+    this.output.gain.setValueAtTime(0.5, p5sound.audiocontext.currentTime);
+    this.oscillator.connect(this.output);
+    // stereo panning
+    this.panPosition = 0;
+    this.connection = p5sound.input;
+    // connect to p5sound by default
+    this.panner = new p5.Panner(this.output, this.connection, 1);
+    //array of math operation signal chaining
+    this.mathOps = [this.output];
+    // add to the soundArray so we can dispose of the osc later
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Start an oscillator. Accepts an optional parameter to
+   *  determine how long (in seconds from now) until the
+   *  oscillator starts.
+   *
+   *  @method  start
+   *  @param  {Number} [time] startTime in seconds from now.
+   *  @param  {Number} [frequency] frequency in Hz.
+   */
+  p5.Oscillator.prototype.start = function (time, f) {
+    if (this.started) {
+      var now = p5sound.audiocontext.currentTime;
+      this.stop(now);
+    }
+    if (!this.started) {
+      var freq = f || this.f;
+      var type = this.oscillator.type;
+      // var detune = this.oscillator.frequency.value;
+      this.oscillator = p5sound.audiocontext.createOscillator();
+      this.oscillator.frequency.exponentialRampToValueAtTime(Math.abs(freq), p5sound.audiocontext.currentTime);
+      this.oscillator.type = type;
+      // this.oscillator.detune.value = detune;
+      this.oscillator.connect(this.output);
+      time = time || 0;
+      this.oscillator.start(time + p5sound.audiocontext.currentTime);
+      this.freqNode = this.oscillator.frequency;
+      // if other oscillators are already connected to this osc's freq
+      for (var i in this._freqMods) {
+        if (typeof this._freqMods[i].connect !== 'undefined') {
+          this._freqMods[i].connect(this.oscillator.frequency);
+        }
+      }
+      this.started = true;
+    }
+  };
+  /**
+   *  Stop an oscillator. Accepts an optional parameter
+   *  to determine how long (in seconds from now) until the
+   *  oscillator stops.
+   *
+   *  @method  stop
+   *  @param  {Number} secondsFromNow Time, in seconds from now.
+   */
+  p5.Oscillator.prototype.stop = function (time) {
+    if (this.started) {
+      var t = time || 0;
+      var now = p5sound.audiocontext.currentTime;
+      this.oscillator.stop(t + now);
+      this.started = false;
+    }
+  };
+  /**
+   *  Set the amplitude between 0 and 1.0. Or, pass in an object
+   *  such as an oscillator to modulate amplitude with an audio signal.
+   *
+   *  @method  amp
+   *  @param  {Number|Object} vol between 0 and 1.0
+   *                              or a modulating signal/oscillator
+   *  @param {Number} [rampTime] create a fade that lasts rampTime 
+   *  @param {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   *  @return  {AudioParam} gain  If no value is provided,
+   *                              returns the Web Audio API
+   *                              AudioParam that controls
+   *                              this oscillator's
+   *                              gain/amplitude/volume)
+   */
+  p5.Oscillator.prototype.amp = function (vol, rampTime, tFromNow) {
+    var self = this;
+    if (typeof vol === 'number') {
+      var rampTime = rampTime || 0;
+      var tFromNow = tFromNow || 0;
+      var now = p5sound.audiocontext.currentTime;
+      var currentVol = this.output.gain.value;
+      this.output.gain.cancelScheduledValues(now);
+      this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
+      this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
+    } else if (vol) {
+      vol.connect(self.output.gain);
+    } else {
+      // return the Gain Node
+      return this.output.gain;
+    }
+  };
+  // these are now the same thing
+  p5.Oscillator.prototype.fade = p5.Oscillator.prototype.amp;
+  p5.Oscillator.prototype.getAmp = function () {
+    return this.output.gain.value;
+  };
+  /**
+   *  Set frequency of an oscillator to a value. Or, pass in an object
+   *  such as an oscillator to modulate the frequency with an audio signal.
+   *
+   *  @method  freq
+   *  @param  {Number|Object} Frequency Frequency in Hz
+   *                                        or modulating signal/oscillator
+   *  @param  {Number} [rampTime] Ramp time (in seconds)
+   *  @param  {Number} [timeFromNow] Schedule this event to happen
+   *                                   at x seconds from now
+   *  @return  {AudioParam} Frequency If no value is provided,
+   *                                  returns the Web Audio API
+   *                                  AudioParam that controls
+   *                                  this oscillator's frequency
+   *  @example
+   *  <div><code>
+   *  var osc = new p5.Oscillator(300);
+   *  osc.start();
+   *  osc.freq(40, 10);
+   *  </code></div>
+   */
+  p5.Oscillator.prototype.freq = function (val, rampTime, tFromNow) {
+    if (typeof val === 'number' && !isNaN(val)) {
+      this.f = val;
+      var now = p5sound.audiocontext.currentTime;
+      var rampTime = rampTime || 0;
+      var tFromNow = tFromNow || 0;
+      // var currentFreq = this.oscillator.frequency.value;
+      // this.oscillator.frequency.cancelScheduledValues(now);
+      if (rampTime == 0) {
+        this.oscillator.frequency.cancelScheduledValues(now);
+        this.oscillator.frequency.setValueAtTime(val, tFromNow + now);
+      } else {
+        if (val > 0) {
+          this.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
+        } else {
+          this.oscillator.frequency.linearRampToValueAtTime(val, tFromNow + rampTime + now);
+        }
+      }
+      // reset phase if oscillator has a phase
+      if (this.phaseAmount) {
+        this.phase(this.phaseAmount);
+      }
+    } else if (val) {
+      if (val.output) {
+        val = val.output;
+      }
+      val.connect(this.oscillator.frequency);
+      // keep track of what is modulating this param
+      // so it can be re-connected if 
+      this._freqMods.push(val);
+    } else {
+      // return the Frequency Node
+      return this.oscillator.frequency;
+    }
+  };
+  p5.Oscillator.prototype.getFreq = function () {
+    return this.oscillator.frequency.value;
+  };
+  /**
+   *  Set type to 'sine', 'triangle', 'sawtooth' or 'square'.
+   *
+   *  @method  setType
+   *  @param {String} type 'sine', 'triangle', 'sawtooth' or 'square'.
+   */
+  p5.Oscillator.prototype.setType = function (type) {
+    this.oscillator.type = type;
+  };
+  p5.Oscillator.prototype.getType = function () {
+    return this.oscillator.type;
+  };
+  /**
+   *  Connect to a p5.sound / Web Audio object.
+   *
+   *  @method  connect
+   *  @param  {Object} unit A p5.sound or Web Audio object
+   */
+  p5.Oscillator.prototype.connect = function (unit) {
+    if (!unit) {
+      this.panner.connect(p5sound.input);
+    } else if (unit.hasOwnProperty('input')) {
+      this.panner.connect(unit.input);
+      this.connection = unit.input;
+    } else {
+      this.panner.connect(unit);
+      this.connection = unit;
+    }
+  };
+  /**
+   *  Disconnect all outputs
+   *
+   *  @method  disconnect
+   */
+  p5.Oscillator.prototype.disconnect = function (unit) {
+    this.output.disconnect();
+    this.panner.disconnect();
+    this.output.connect(this.panner);
+    this.oscMods = [];
+  };
+  /**
+   *  Pan between Left (-1) and Right (1)
+   *
+   *  @method  pan
+   *  @param  {Number} panning Number between -1 and 1
+   *  @param  {Number} timeFromNow schedule this event to happen
+   *                                seconds from now
+   */
+  p5.Oscillator.prototype.pan = function (pval, tFromNow) {
+    this.panPosition = pval;
+    this.panner.pan(pval, tFromNow);
+  };
+  p5.Oscillator.prototype.getPan = function () {
+    return this.panPosition;
+  };
+  // get rid of the oscillator
+  p5.Oscillator.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    if (this.oscillator) {
+      var now = p5sound.audiocontext.currentTime;
+      this.stop(now);
+      this.disconnect();
+      this.oscillator.disconnect();
+      this.panner = null;
+      this.oscillator = null;
+    }
+    // if it is a Pulse
+    if (this.osc2) {
+      this.osc2.dispose();
+    }
+  };
+  /**
+   *  Set the phase of an oscillator between 0.0 and 1.0.
+   *  In this implementation, phase is a delay time
+   *  based on the oscillator's current frequency.
+   *  
+   *  @method  phase
+   *  @param  {Number} phase float between 0.0 and 1.0
+   */
+  p5.Oscillator.prototype.phase = function (p) {
+    var delayAmt = p5.prototype.map(p, 0, 1, 0, 1 / this.f);
+    var now = p5sound.audiocontext.currentTime;
+    this.phaseAmount = p;
+    if (!this.dNode) {
+      // create a delay node
+      this.dNode = p5sound.audiocontext.createDelay();
+      // put the delay node in between output and panner
+      this.oscillator.disconnect();
+      this.oscillator.connect(this.dNode);
+      this.dNode.connect(this.output);
+    }
+    // set delay time to match phase:
+    this.dNode.delayTime.setValueAtTime(delayAmt, now);
+  };
+  // ========================== //
+  // SIGNAL MATH FOR MODULATION //
+  // ========================== //
+  // return sigChain(this, scale, thisChain, nextChain, Scale);
+  var sigChain = function (o, mathObj, thisChain, nextChain, type) {
+    var chainSource = o.oscillator;
+    // if this type of math already exists in the chain, replace it
+    for (var i in o.mathOps) {
+      if (o.mathOps[i] instanceof type) {
+        chainSource.disconnect();
+        o.mathOps[i].dispose();
+        thisChain = i;
+        // assume nextChain is output gain node unless...
+        if (thisChain < o.mathOps.length - 2) {
+          nextChain = o.mathOps[i + 1];
+        }
+      }
+    }
+    if (thisChain == o.mathOps.length - 1) {
+      o.mathOps.push(nextChain);
+    }
+    // assume source is the oscillator unless i > 0
+    if (i > 0) {
+      chainSource = o.mathOps[i - 1];
+    }
+    chainSource.disconnect();
+    chainSource.connect(mathObj);
+    mathObj.connect(nextChain);
+    o.mathOps[thisChain] = mathObj;
+    return o;
+  };
+  /**
+   *  Add a value to the p5.Oscillator's output amplitude,
+   *  and return the oscillator. Calling this method again
+   *  will override the initial add() with a new value.
+   *  
+   *  @method  add
+   *  @param {Number} number Constant number to add
+   *  @return {p5.Oscillator} Oscillator Returns this oscillator
+   *                                     with scaled output
+   *  
+   */
+  p5.Oscillator.prototype.add = function (num) {
+    var add = new Add(num);
+    var thisChain = this.mathOps.length - 1;
+    var nextChain = this.output;
+    return sigChain(this, add, thisChain, nextChain, Add);
+  };
+  /**
+   *  Multiply the p5.Oscillator's output amplitude
+   *  by a fixed value (i.e. turn it up!). Calling this method
+   *  again will override the initial mult() with a new value.
+   *  
+   *  @method  mult
+   *  @param {Number} number Constant number to multiply
+   *  @return {p5.Oscillator} Oscillator Returns this oscillator
+   *                                     with multiplied output
+   */
+  p5.Oscillator.prototype.mult = function (num) {
+    var mult = new Mult(num);
+    var thisChain = this.mathOps.length - 1;
+    var nextChain = this.output;
+    return sigChain(this, mult, thisChain, nextChain, Mult);
+  };
+  /**
+   *  Scale this oscillator's amplitude values to a given
+   *  range, and return the oscillator. Calling this method
+   *  again will override the initial scale() with new values.
+   *  
+   *  @method  scale
+   *  @param  {Number} inMin  input range minumum
+   *  @param  {Number} inMax  input range maximum
+   *  @param  {Number} outMin input range minumum
+   *  @param  {Number} outMax input range maximum
+   *  @return {p5.Oscillator} Oscillator Returns this oscillator
+   *                                     with scaled output
+   */
+  p5.Oscillator.prototype.scale = function (inMin, inMax, outMin, outMax) {
+    var mapOutMin, mapOutMax;
+    if (arguments.length === 4) {
+      mapOutMin = p5.prototype.map(outMin, inMin, inMax, 0, 1) - 0.5;
+      mapOutMax = p5.prototype.map(outMax, inMin, inMax, 0, 1) - 0.5;
+    } else {
+      mapOutMin = arguments[0];
+      mapOutMax = arguments[1];
+    }
+    var scale = new Scale(mapOutMin, mapOutMax);
+    var thisChain = this.mathOps.length - 1;
+    var nextChain = this.output;
+    return sigChain(this, scale, thisChain, nextChain, Scale);
+  };
+  // ============================== //
+  // SinOsc, TriOsc, SqrOsc, SawOsc //
+  // ============================== //
+  /**
+   *  Constructor: <code>new p5.SinOsc()</code>.
+   *  This creates a Sine Wave Oscillator and is
+   *  equivalent to <code> new p5.Oscillator('sine')
+   *  </code> or creating a p5.Oscillator and then calling
+   *  its method <code>setType('sine')</code>.
+   *  See p5.Oscillator for methods.
+   *
+   *  @method  p5.SinOsc
+   *  @param {[Number]} freq Set the frequency
+   */
+  p5.SinOsc = function (freq) {
+    p5.Oscillator.call(this, freq, 'sine');
+  };
+  p5.SinOsc.prototype = Object.create(p5.Oscillator.prototype);
+  /**
+   *  Constructor: <code>new p5.TriOsc()</code>.
+   *  This creates a Triangle Wave Oscillator and is
+   *  equivalent to <code>new p5.Oscillator('triangle')
+   *  </code> or creating a p5.Oscillator and then calling
+   *  its method <code>setType('triangle')</code>.
+   *  See p5.Oscillator for methods.
+   *
+   *  @method  p5.TriOsc
+   *  @param {[Number]} freq Set the frequency
+   */
+  p5.TriOsc = function (freq) {
+    p5.Oscillator.call(this, freq, 'triangle');
+  };
+  p5.TriOsc.prototype = Object.create(p5.Oscillator.prototype);
+  /**
+   *  Constructor: <code>new p5.SawOsc()</code>.
+   *  This creates a SawTooth Wave Oscillator and is
+   *  equivalent to <code> new p5.Oscillator('sawtooth')
+   *  </code> or creating a p5.Oscillator and then calling
+   *  its method <code>setType('sawtooth')</code>.
+   *  See p5.Oscillator for methods.
+   *
+   *  @method  p5.SawOsc
+   *  @param {[Number]} freq Set the frequency
+   */
+  p5.SawOsc = function (freq) {
+    p5.Oscillator.call(this, freq, 'sawtooth');
+  };
+  p5.SawOsc.prototype = Object.create(p5.Oscillator.prototype);
+  /**
+   *  Constructor: <code>new p5.SqrOsc()</code>.
+   *  This creates a Square Wave Oscillator and is
+   *  equivalent to <code> new p5.Oscillator('square')
+   *  </code> or creating a p5.Oscillator and then calling
+   *  its method <code>setType('square')</code>.
+   *  See p5.Oscillator for methods.
+   *
+   *  @method  p5.SqrOsc
+   *  @param {[Number]} freq Set the frequency
+   */
+  p5.SqrOsc = function (freq) {
+    p5.Oscillator.call(this, freq, 'square');
+  };
+  p5.SqrOsc.prototype = Object.create(p5.Oscillator.prototype);
+}(master, Tone_signal_Signal, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_core_Timeline;
+Tone_core_Timeline = function (Tone) {
+  'use strict';
+  Tone.Timeline = function () {
+    var options = this.optionsObject(arguments, ['memory'], Tone.Timeline.defaults);
+    this._timeline = [];
+    this._toRemove = [];
+    this._iterating = false;
+    this.memory = options.memory;
+  };
+  Tone.extend(Tone.Timeline);
+  Tone.Timeline.defaults = { 'memory': Infinity };
+  Object.defineProperty(Tone.Timeline.prototype, 'length', {
+    get: function () {
+      return this._timeline.length;
+    }
+  });
+  Tone.Timeline.prototype.addEvent = function (event) {
+    if (this.isUndef(event.time)) {
+      throw new Error('events must have a time attribute');
+    }
+    event.time = this.toSeconds(event.time);
+    if (this._timeline.length) {
+      var index = this._search(event.time);
+      this._timeline.splice(index + 1, 0, event);
+    } else {
+      this._timeline.push(event);
+    }
+    if (this.length > this.memory) {
+      var diff = this.length - this.memory;
+      this._timeline.splice(0, diff);
+    }
+    return this;
+  };
+  Tone.Timeline.prototype.removeEvent = function (event) {
+    if (this._iterating) {
+      this._toRemove.push(event);
+    } else {
+      var index = this._timeline.indexOf(event);
+      if (index !== -1) {
+        this._timeline.splice(index, 1);
+      }
+    }
+    return this;
+  };
+  Tone.Timeline.prototype.getEvent = function (time) {
+    time = this.toSeconds(time);
+    var index = this._search(time);
+    if (index !== -1) {
+      return this._timeline[index];
+    } else {
+      return null;
+    }
+  };
+  Tone.Timeline.prototype.getEventAfter = function (time) {
+    time = this.toSeconds(time);
+    var index = this._search(time);
+    if (index + 1 < this._timeline.length) {
+      return this._timeline[index + 1];
+    } else {
+      return null;
+    }
+  };
+  Tone.Timeline.prototype.getEventBefore = function (time) {
+    time = this.toSeconds(time);
+    var index = this._search(time);
+    if (index - 1 >= 0) {
+      return this._timeline[index - 1];
+    } else {
+      return null;
+    }
+  };
+  Tone.Timeline.prototype.cancel = function (after) {
+    if (this._timeline.length > 1) {
+      after = this.toSeconds(after);
+      var index = this._search(after);
+      if (index >= 0) {
+        this._timeline = this._timeline.slice(0, index);
+      } else {
+        this._timeline = [];
+      }
+    } else if (this._timeline.length === 1) {
+      if (this._timeline[0].time >= after) {
+        this._timeline = [];
+      }
+    }
+    return this;
+  };
+  Tone.Timeline.prototype.cancelBefore = function (time) {
+    if (this._timeline.length) {
+      time = this.toSeconds(time);
+      var index = this._search(time);
+      if (index >= 0) {
+        this._timeline = this._timeline.slice(index + 1);
+      }
+    }
+    return this;
+  };
+  Tone.Timeline.prototype._search = function (time) {
+    var beginning = 0;
+    var len = this._timeline.length;
+    var end = len;
+    while (beginning <= end && beginning < len) {
+      var midPoint = Math.floor(beginning + (end - beginning) / 2);
+      var event = this._timeline[midPoint];
+      if (event.time === time) {
+        for (var i = midPoint; i < this._timeline.length; i++) {
+          var testEvent = this._timeline[i];
+          if (testEvent.time === time) {
+            midPoint = i;
+          }
+        }
+        return midPoint;
+      } else if (event.time > time) {
+        end = midPoint - 1;
+      } else if (event.time < time) {
+        beginning = midPoint + 1;
+      }
+    }
+    return beginning - 1;
+  };
+  Tone.Timeline.prototype._iterate = function (callback, lowerBound, upperBound) {
+    this._iterating = true;
+    lowerBound = this.defaultArg(lowerBound, 0);
+    upperBound = this.defaultArg(upperBound, this._timeline.length - 1);
+    for (var i = lowerBound; i <= upperBound; i++) {
+      callback(this._timeline[i]);
+    }
+    this._iterating = false;
+    if (this._toRemove.length > 0) {
+      for (var j = 0; j < this._toRemove.length; j++) {
+        var index = this._timeline.indexOf(this._toRemove[j]);
+        if (index !== -1) {
+          this._timeline.splice(index, 1);
+        }
+      }
+      this._toRemove = [];
+    }
+  };
+  Tone.Timeline.prototype.forEach = function (callback) {
+    this._iterate(callback);
+    return this;
+  };
+  Tone.Timeline.prototype.forEachBefore = function (time, callback) {
+    time = this.toSeconds(time);
+    var upperBound = this._search(time);
+    if (upperBound !== -1) {
+      this._iterate(callback, 0, upperBound);
+    }
+    return this;
+  };
+  Tone.Timeline.prototype.forEachAfter = function (time, callback) {
+    time = this.toSeconds(time);
+    var lowerBound = this._search(time);
+    this._iterate(callback, lowerBound + 1);
+    return this;
+  };
+  Tone.Timeline.prototype.forEachFrom = function (time, callback) {
+    time = this.toSeconds(time);
+    var lowerBound = this._search(time);
+    while (lowerBound >= 0 && this._timeline[lowerBound].time >= time) {
+      lowerBound--;
+    }
+    this._iterate(callback, lowerBound + 1);
+    return this;
+  };
+  Tone.Timeline.prototype.forEachAtTime = function (time, callback) {
+    time = this.toSeconds(time);
+    var upperBound = this._search(time);
+    if (upperBound !== -1) {
+      this._iterate(function (event) {
+        if (event.time === time) {
+          callback(event);
+        }
+      }, 0, upperBound);
+    }
+    return this;
+  };
+  Tone.Timeline.prototype.dispose = function () {
+    Tone.prototype.dispose.call(this);
+    this._timeline = null;
+    this._toRemove = null;
+  };
+  return Tone.Timeline;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_signal_TimelineSignal;
+Tone_signal_TimelineSignal = function (Tone) {
+  'use strict';
+  Tone.TimelineSignal = function () {
+    var options = this.optionsObject(arguments, [
+      'value',
+      'units'
+    ], Tone.Signal.defaults);
+    Tone.Signal.apply(this, options);
+    options.param = this._param;
+    Tone.Param.call(this, options);
+    this._events = new Tone.Timeline(10);
+    this._initial = this._fromUnits(this._param.value);
+  };
+  Tone.extend(Tone.TimelineSignal, Tone.Param);
+  Tone.TimelineSignal.Type = {
+    Linear: 'linear',
+    Exponential: 'exponential',
+    Target: 'target',
+    Set: 'set'
+  };
+  Object.defineProperty(Tone.TimelineSignal.prototype, 'value', {
+    get: function () {
+      return this._toUnits(this._param.value);
+    },
+    set: function (value) {
+      var convertedVal = this._fromUnits(value);
+      this._initial = convertedVal;
+      this._param.value = convertedVal;
+    }
+  });
+  Tone.TimelineSignal.prototype.setValueAtTime = function (value, startTime) {
+    value = this._fromUnits(value);
+    startTime = this.toSeconds(startTime);
+    this._events.addEvent({
+      'type': Tone.TimelineSignal.Type.Set,
+      'value': value,
+      'time': startTime
+    });
+    this._param.setValueAtTime(value, startTime);
+    return this;
+  };
+  Tone.TimelineSignal.prototype.linearRampToValueAtTime = function (value, endTime) {
+    value = this._fromUnits(value);
+    endTime = this.toSeconds(endTime);
+    this._events.addEvent({
+      'type': Tone.TimelineSignal.Type.Linear,
+      'value': value,
+      'time': endTime
+    });
+    this._param.linearRampToValueAtTime(value, endTime);
+    return this;
+  };
+  Tone.TimelineSignal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
+    value = this._fromUnits(value);
+    value = Math.max(this._minOutput, value);
+    endTime = this.toSeconds(endTime);
+    this._events.addEvent({
+      'type': Tone.TimelineSignal.Type.Exponential,
+      'value': value,
+      'time': endTime
+    });
+    this._param.exponentialRampToValueAtTime(value, endTime);
+    return this;
+  };
+  Tone.TimelineSignal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
+    value = this._fromUnits(value);
+    value = Math.max(this._minOutput, value);
+    timeConstant = Math.max(this._minOutput, timeConstant);
+    startTime = this.toSeconds(startTime);
+    this._events.addEvent({
+      'type': Tone.TimelineSignal.Type.Target,
+      'value': value,
+      'time': startTime,
+      'constant': timeConstant
+    });
+    this._param.setTargetAtTime(value, startTime, timeConstant);
+    return this;
+  };
+  Tone.TimelineSignal.prototype.cancelScheduledValues = function (after) {
+    this._events.cancel(after);
+    this._param.cancelScheduledValues(this.toSeconds(after));
+    return this;
+  };
+  Tone.TimelineSignal.prototype.setRampPoint = function (time) {
+    time = this.toSeconds(time);
+    var val = this.getValueAtTime(time);
+    var after = this._searchAfter(time);
+    if (after) {
+      this.cancelScheduledValues(time);
+      if (after.type === Tone.TimelineSignal.Type.Linear) {
+        this.linearRampToValueAtTime(val, time);
+      } else if (after.type === Tone.TimelineSignal.Type.Exponential) {
+        this.exponentialRampToValueAtTime(val, time);
+      }
+    }
+    this.setValueAtTime(val, time);
+    return this;
+  };
+  Tone.TimelineSignal.prototype.linearRampToValueBetween = function (value, start, finish) {
+    this.setRampPoint(start);
+    this.linearRampToValueAtTime(value, finish);
+    return this;
+  };
+  Tone.TimelineSignal.prototype.exponentialRampToValueBetween = function (value, start, finish) {
+    this.setRampPoint(start);
+    this.exponentialRampToValueAtTime(value, finish);
+    return this;
+  };
+  Tone.TimelineSignal.prototype._searchBefore = function (time) {
+    return this._events.getEvent(time);
+  };
+  Tone.TimelineSignal.prototype._searchAfter = function (time) {
+    return this._events.getEventAfter(time);
+  };
+  Tone.TimelineSignal.prototype.getValueAtTime = function (time) {
+    var after = this._searchAfter(time);
+    var before = this._searchBefore(time);
+    var value = this._initial;
+    if (before === null) {
+      value = this._initial;
+    } else if (before.type === Tone.TimelineSignal.Type.Target) {
+      var previous = this._events.getEventBefore(before.time);
+      var previouVal;
+      if (previous === null) {
+        previouVal = this._initial;
+      } else {
+        previouVal = previous.value;
+      }
+      value = this._exponentialApproach(before.time, previouVal, before.value, before.constant, time);
+    } else if (after === null) {
+      value = before.value;
+    } else if (after.type === Tone.TimelineSignal.Type.Linear) {
+      value = this._linearInterpolate(before.time, before.value, after.time, after.value, time);
+    } else if (after.type === Tone.TimelineSignal.Type.Exponential) {
+      value = this._exponentialInterpolate(before.time, before.value, after.time, after.value, time);
+    } else {
+      value = before.value;
+    }
+    return value;
+  };
+  Tone.TimelineSignal.prototype.connect = Tone.SignalBase.prototype.connect;
+  Tone.TimelineSignal.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
+    return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
+  };
+  Tone.TimelineSignal.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
+    return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
+  };
+  Tone.TimelineSignal.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
+    v0 = Math.max(this._minOutput, v0);
+    return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
+  };
+  Tone.TimelineSignal.prototype.dispose = function () {
+    Tone.Signal.prototype.dispose.call(this);
+    Tone.Param.prototype.dispose.call(this);
+    this._events.dispose();
+    this._events = null;
+  };
+  return Tone.TimelineSignal;
+}(Tone_core_Tone, Tone_signal_Signal);
+var env;
+env = function () {
+  'use strict';
+  var p5sound = master;
+  var Add = Tone_signal_Add;
+  var Mult = Tone_signal_Multiply;
+  var Scale = Tone_signal_Scale;
+  var TimelineSignal = Tone_signal_TimelineSignal;
+  var Tone = Tone_core_Tone;
+  Tone.setContext(p5sound.audiocontext);
+  /**
+   *  <p>Envelopes are pre-defined amplitude distribution over time.
+   *  Typically, envelopes are used to control the output volume
+   *  of an object, a series of fades referred to as Attack, Decay,
+   *  Sustain and Release (
+   *  <a href="https://upload.wikimedia.org/wikipedia/commons/e/ea/ADSR_parameter.svg">ADSR</a>
+   *  ). Envelopes can also control other Web Audio Parameters—for example, a p5.Env can 
+   *  control an Oscillator's frequency like this: <code>osc.freq(env)</code>.</p>
+   *  <p>Use <code><a href="#/p5.Env/setRange">setRange</a></code> to change the attack/release level.
+   *  Use <code><a href="#/p5.Env/setADSR">setADSR</a></code> to change attackTime, decayTime, sustainPercent and releaseTime.</p>
+   *  <p>Use the <code><a href="#/p5.Env/play">play</a></code> method to play the entire envelope,
+   *  the <code><a href="#/p5.Env/ramp">ramp</a></code> method for a pingable trigger,
+   *  or <code><a href="#/p5.Env/triggerAttack">triggerAttack</a></code>/
+   *  <code><a href="#/p5.Env/triggerRelease">triggerRelease</a></code> to trigger noteOn/noteOff.</p>
+   *  
+   *  @class p5.Env
+   *  @constructor
+   *  @example
+   *  <div><code>
+   *  var attackLevel = 1.0;
+   *  var releaseLevel = 0;
+   *
+   *  var attackTime = 0.001
+   *  var decayTime = 0.2;
+   *  var susPercent = 0.2;
+   *  var releaseTime = 0.5;
+   *
+   *  var env, triOsc;
+   *
+   *  function setup() {
+   *    var cnv = createCanvas(100, 100);
+   *
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *
+   *    env = new p5.Env();
+   *    env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+   *    env.setRange(attackLevel, releaseLevel);
+   *
+   *    triOsc = new p5.Oscillator('triangle');
+   *    triOsc.amp(env);
+   *    triOsc.start();
+   *    triOsc.freq(220);
+   *
+   *    cnv.mousePressed(playEnv);
+   *  }
+   *
+   *  function playEnv(){
+   *    env.play();
+   *  }
+   *  </code></div>
+   */
+  p5.Env = function (t1, l1, t2, l2, t3, l3) {
+    var now = p5sound.audiocontext.currentTime;
+    /**
+     * Time until envelope reaches attackLevel
+     * @property attackTime
+     */
+    this.aTime = t1 || 0.1;
+    /**
+     * Level once attack is complete.
+     * @property attackLevel
+     */
+    this.aLevel = l1 || 1;
+    /**
+     * Time until envelope reaches decayLevel.
+     * @property decayTime
+     */
+    this.dTime = t2 || 0.5;
+    /**
+     * Level after decay. The envelope will sustain here until it is released.
+     * @property decayLevel
+     */
+    this.dLevel = l2 || 0;
+    /**
+     * Duration of the release portion of the envelope.
+     * @property releaseTime
+     */
+    this.rTime = t3 || 0;
+    /**
+     * Level at the end of the release.
+     * @property releaseLevel
+     */
+    this.rLevel = l3 || 0;
+    this._rampHighPercentage = 0.98;
+    this._rampLowPercentage = 0.02;
+    this.output = p5sound.audiocontext.createGain();
+    this.control = new TimelineSignal();
+    this._init();
+    // this makes sure the envelope starts at zero
+    this.control.connect(this.output);
+    // connect to the output
+    this.connection = null;
+    // store connection
+    //array of math operation signal chaining
+    this.mathOps = [this.control];
+    //whether envelope should be linear or exponential curve
+    this.isExponential = false;
+    // oscillator or buffer source to clear on env complete
+    // to save resources if/when it is retriggered
+    this.sourceToClear = null;
+    // set to true if attack is set, then false on release
+    this.wasTriggered = false;
+    // add to the soundArray so we can dispose of the env later
+    p5sound.soundArray.push(this);
+  };
+  // this init function just smooths the starting value to zero and gives a start point for the timeline
+  // - it was necessary to remove glitches at the beginning.
+  p5.Env.prototype._init = function () {
+    var now = p5sound.audiocontext.currentTime;
+    var t = now;
+    this.control.setTargetAtTime(0.00001, t, 0.001);
+    //also, compute the correct time constants
+    this._setRampAD(this.aTime, this.dTime);
+  };
+  /**
+   *  Reset the envelope with a series of time/value pairs.
+   *
+   *  @method  set
+   *  @param {Number} attackTime     Time (in seconds) before level
+   *                                 reaches attackLevel
+   *  @param {Number} attackLevel    Typically an amplitude between
+   *                                 0.0 and 1.0
+   *  @param {Number} decayTime      Time
+   *  @param {Number} decayLevel   Amplitude (In a standard ADSR envelope,
+   *                                 decayLevel = sustainLevel)
+   *  @param {Number} releaseTime   Release Time (in seconds)
+   *  @param {Number} releaseLevel  Amplitude
+   */
+  p5.Env.prototype.set = function (t1, l1, t2, l2, t3, l3) {
+    this.aTime = t1;
+    this.aLevel = l1;
+    this.dTime = t2 || 0;
+    this.dLevel = l2 || 0;
+    this.rTime = t4 || 0;
+    this.rLevel = l4 || 0;
+    // set time constants for ramp
+    this._setRampAD(t1, t2);
+  };
+  /**
+   *  Set values like a traditional
+   *  <a href="https://en.wikipedia.org/wiki/Synthesizer#/media/File:ADSR_parameter.svg">
+   *  ADSR envelope
+   *  </a>.
+   *  
+   *  @method  setADSR
+   *  @param {Number} attackTime    Time (in seconds before envelope
+   *                                reaches Attack Level
+   *  @param {Number} [decayTime]    Time (in seconds) before envelope
+   *                                reaches Decay/Sustain Level
+   *  @param {Number} [susRatio]    Ratio between attackLevel and releaseLevel, on a scale from 0 to 1,
+   *                                where 1.0 = attackLevel, 0.0 = releaseLevel.
+   *                                The susRatio determines the decayLevel and the level at which the
+   *                                sustain portion of the envelope will sustain.
+   *                                For example, if attackLevel is 0.4, releaseLevel is 0,
+   *                                and susAmt is 0.5, the decayLevel would be 0.2. If attackLevel is
+   *                                increased to 1.0 (using <code>setRange</code>),
+   *                                then decayLevel would increase proportionally, to become 0.5.
+   *  @param {Number} [releaseTime]   Time in seconds from now (defaults to 0)
+   *  @example
+   *  <div><code>
+   *  var attackLevel = 1.0;
+   *  var releaseLevel = 0;
+   *
+   *  var attackTime = 0.001
+   *  var decayTime = 0.2;
+   *  var susPercent = 0.2;
+   *  var releaseTime = 0.5;
+   *
+   *  var env, triOsc;
+   *
+   *  function setup() {
+   *    var cnv = createCanvas(100, 100);
+   *
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *
+   *    env = new p5.Env();
+   *    env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+   *    env.setRange(attackLevel, releaseLevel);
+   *
+   *    triOsc = new p5.Oscillator('triangle');
+   *    triOsc.amp(env);
+   *    triOsc.start();
+   *    triOsc.freq(220);
+   *
+   *    cnv.mousePressed(playEnv);
+   *  }
+   *
+   *  function playEnv(){
+   *    env.play();
+   *  }
+   *  </code></div>
+   */
+  p5.Env.prototype.setADSR = function (aTime, dTime, sPercent, rTime) {
+    this.aTime = aTime;
+    this.dTime = dTime || 0;
+    // lerp
+    this.sPercent = sPercent || 0;
+    this.dLevel = typeof sPercent !== 'undefined' ? sPercent * (this.aLevel - this.rLevel) + this.rLevel : 0;
+    this.rTime = rTime || 0;
+    // also set time constants for ramp
+    this._setRampAD(aTime, dTime);
+  };
+  /**
+   *  Set max (attackLevel) and min (releaseLevel) of envelope.
+   *  
+   *  @method  setRange
+   *  @param {Number} aLevel attack level (defaults to 1)
+   *  @param {Number} rLevel release level (defaults to 0)
+   *  @example
+   *  <div><code>
+   *  var attackLevel = 1.0;
+   *  var releaseLevel = 0;
+   *
+   *  var attackTime = 0.001
+   *  var decayTime = 0.2;
+   *  var susPercent = 0.2;
+   *  var releaseTime = 0.5;
+   *
+   *  var env, triOsc;
+   *
+   *  function setup() {
+   *    var cnv = createCanvas(100, 100);
+   *
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *
+   *    env = new p5.Env();
+   *    env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+   *    env.setRange(attackLevel, releaseLevel);
+   *
+   *    triOsc = new p5.Oscillator('triangle');
+   *    triOsc.amp(env);
+   *    triOsc.start();
+   *    triOsc.freq(220);
+   *
+   *    cnv.mousePressed(playEnv);
+   *  }
+   *
+   *  function playEnv(){
+   *    env.play();
+   *  }
+   *  </code></div>
+   */
+  p5.Env.prototype.setRange = function (aLevel, rLevel) {
+    this.aLevel = aLevel || 1;
+    this.rLevel = rLevel || 0;
+  };
+  //  private (undocumented) method called when ADSR is set to set time constants for ramp
+  //
+  //  Set the <a href="https://en.wikipedia.org/wiki/RC_time_constant">
+  //  time constants</a> for simple exponential ramps.
+  //  The larger the time constant value, the slower the
+  //  transition will be.
+  //
+  //  method  _setRampAD
+  //  param {Number} attackTimeConstant  attack time constant
+  //  param {Number} decayTimeConstant   decay time constant
+  //
+  p5.Env.prototype._setRampAD = function (t1, t2) {
+    this._rampAttackTime = this.checkExpInput(t1);
+    this._rampDecayTime = this.checkExpInput(t2);
+    var TCDenominator = 1;
+    /// Aatish Bhatia's calculation for time constant for rise(to adjust 1/1-e calculation to any percentage)
+    TCDenominator = Math.log(1 / this.checkExpInput(1 - this._rampHighPercentage));
+    this._rampAttackTC = t1 / this.checkExpInput(TCDenominator);
+    TCDenominator = Math.log(1 / this._rampLowPercentage);
+    this._rampDecayTC = t2 / this.checkExpInput(TCDenominator);
+  };
+  // private method
+  p5.Env.prototype.setRampPercentages = function (p1, p2) {
+    //set the percentages that the simple exponential ramps go to
+    this._rampHighPercentage = this.checkExpInput(p1);
+    this._rampLowPercentage = this.checkExpInput(p2);
+    var TCDenominator = 1;
+    //now re-compute the time constants based on those percentages
+    /// Aatish Bhatia's calculation for time constant for rise(to adjust 1/1-e calculation to any percentage)
+    TCDenominator = Math.log(1 / this.checkExpInput(1 - this._rampHighPercentage));
+    this._rampAttackTC = this._rampAttackTime / this.checkExpInput(TCDenominator);
+    TCDenominator = Math.log(1 / this._rampLowPercentage);
+    this._rampDecayTC = this._rampDecayTime / this.checkExpInput(TCDenominator);
+  };
+  /**
+   *  Assign a parameter to be controlled by this envelope.
+   *  If a p5.Sound object is given, then the p5.Env will control its
+   *  output gain. If multiple inputs are provided, the env will
+   *  control all of them.
+   *  
+   *  @method  setInput
+   *  @param  {Object} unit         A p5.sound object or
+   *                                Web Audio Param.
+   */
+  p5.Env.prototype.setInput = function (unit) {
+    for (var i = 0; i < arguments.length; i++) {
+      this.connect(arguments[i]);
+    }
+  };
+  /**
+   *  Set whether the envelope ramp is linear (default) or exponential.
+   *  Exponential ramps can be useful because we perceive amplitude
+   *  and frequency logarithmically.
+   *  
+   *  @method  setExp
+   *  @param {Boolean} isExp true is exponential, false is linear
+   */
+  p5.Env.prototype.setExp = function (isExp) {
+    this.isExponential = isExp;
+  };
+  //helper method to protect against zero values being sent to exponential functions
+  p5.Env.prototype.checkExpInput = function (value) {
+    if (value <= 0) {
+      value = 0.0001;
+    }
+    return value;
+  };
+  /**
+   *  Play tells the envelope to start acting on a given input.
+   *  If the input is a p5.sound object (i.e. AudioIn, Oscillator,
+   *  SoundFile), then Env will control its output volume.
+   *  Envelopes can also be used to control any <a href="
+   *  http://docs.webplatform.org/wiki/apis/webaudio/AudioParam">
+   *  Web Audio Audio Param.</a>
+   *
+   *  @method  play
+   *  @param  {Object} unit         A p5.sound object or
+   *                                Web Audio Param.
+   *  @param  {Number} [startTime]  time from now (in seconds) at which to play
+   *  @param  {Number} [sustainTime] time to sustain before releasing the envelope
+   *  @example
+   *  <div><code>
+   *  var attackLevel = 1.0;
+   *  var releaseLevel = 0;
+   *
+   *  var attackTime = 0.001
+   *  var decayTime = 0.2;
+   *  var susPercent = 0.2;
+   *  var releaseTime = 0.5;
+   *
+   *  var env, triOsc;
+   *
+   *  function setup() {
+   *    var cnv = createCanvas(100, 100);
+   *
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *
+   *    env = new p5.Env();
+   *    env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+   *    env.setRange(attackLevel, releaseLevel);
+   *
+   *    triOsc = new p5.Oscillator('triangle');
+   *    triOsc.amp(env);
+   *    triOsc.start();
+   *    triOsc.freq(220);
+   *
+   *    cnv.mousePressed(playEnv);
+   *  }
+   *
+   *  function playEnv(){
+   *    // trigger env on triOsc, 0 seconds from now
+   *    // After decay, sustain for 0.2 seconds before release
+   *    env.play(triOsc, 0, 0.2);
+   *  }
+   *  </code></div>
+   */
+  p5.Env.prototype.play = function (unit, secondsFromNow, susTime) {
+    var now = p5sound.audiocontext.currentTime;
+    var tFromNow = secondsFromNow || 0;
+    var susTime = susTime || 0;
+    if (unit) {
+      if (this.connection !== unit) {
+        this.connect(unit);
+      }
+    }
+    this.triggerAttack(unit, tFromNow);
+    this.triggerRelease(unit, tFromNow + this.aTime + this.dTime + susTime);
+  };
+  /**
+   *  Trigger the Attack, and Decay portion of the Envelope.
+   *  Similar to holding down a key on a piano, but it will
+   *  hold the sustain level until you let go. Input can be
+   *  any p5.sound object, or a <a href="
+   *  http://docs.webplatform.org/wiki/apis/webaudio/AudioParam">
+   *  Web Audio Param</a>.
+   *
+   *  @method  triggerAttack
+   *  @param  {Object} unit p5.sound Object or Web Audio Param
+   *  @param  {Number} secondsFromNow time from now (in seconds)
+   *  @example
+   *  <div><code>
+   *  
+   *  var attackLevel = 1.0;
+   *  var releaseLevel = 0;
+   *  
+   *  var attackTime = 0.001
+   *  var decayTime = 0.3;
+   *  var susPercent = 0.4;
+   *  var releaseTime = 0.5;
+   *  
+   *  var env, triOsc;
+   *  
+   *  function setup() {
+   *    var cnv = createCanvas(100, 100);
+   *    background(200);
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *  
+   *    env = new p5.Env();
+   *    env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+   *    env.setRange(attackLevel, releaseLevel);
+   *  
+   *    triOsc = new p5.Oscillator('triangle');
+   *    triOsc.amp(env);
+   *    triOsc.start();
+   *    triOsc.freq(220);
+   *  
+   *    cnv.mousePressed(envAttack);
+   *  }
+   *  
+   *  function envAttack(){
+   *    console.log('trigger attack');
+   *    env.triggerAttack();
+   *  
+   *    background(0,255,0);
+   *    text('attack!', width/2, height/2);
+   *  }
+   *  
+   *  function mouseReleased() {
+   *    env.triggerRelease();
+   *  
+   *    background(200);
+   *    text('click to play', width/2, height/2);
+   *  }
+   *  </code></div>
+   */
+  p5.Env.prototype.triggerAttack = function (unit, secondsFromNow) {
+    var now = p5sound.audiocontext.currentTime;
+    var tFromNow = secondsFromNow || 0;
+    var t = now + tFromNow;
+    this.lastAttack = t;
+    this.wasTriggered = true;
+    if (unit) {
+      if (this.connection !== unit) {
+        this.connect(unit);
+      }
+    }
+    // get and set value (with linear ramp) to anchor automation
+    var valToSet = this.control.getValueAtTime(t);
+    this.control.cancelScheduledValues(t);
+    // not sure if this is necessary
+    if (this.isExponential == true) {
+      this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
+    } else {
+      this.control.linearRampToValueAtTime(valToSet, t);
+    }
+    // after each ramp completes, cancel scheduled values
+    // (so they can be overridden in case env has been re-triggered)
+    // then, set current value (with linearRamp to avoid click)
+    // then, schedule the next automation...
+    // attack
+    t += this.aTime;
+    if (this.isExponential == true) {
+      this.control.exponentialRampToValueAtTime(this.checkExpInput(this.aLevel), t);
+      valToSet = this.checkExpInput(this.control.getValueAtTime(t));
+      this.control.cancelScheduledValues(t);
+      this.control.exponentialRampToValueAtTime(valToSet, t);
+    } else {
+      this.control.linearRampToValueAtTime(this.aLevel, t);
+      valToSet = this.control.getValueAtTime(t);
+      this.control.cancelScheduledValues(t);
+      this.control.linearRampToValueAtTime(valToSet, t);
+    }
+    // decay to decay level (if using ADSR, then decay level == sustain level)
+    t += this.dTime;
+    if (this.isExponential == true) {
+      this.control.exponentialRampToValueAtTime(this.checkExpInput(this.dLevel), t);
+      valToSet = this.checkExpInput(this.control.getValueAtTime(t));
+      this.control.cancelScheduledValues(t);
+      this.control.exponentialRampToValueAtTime(valToSet, t);
+    } else {
+      this.control.linearRampToValueAtTime(this.dLevel, t);
+      valToSet = this.control.getValueAtTime(t);
+      this.control.cancelScheduledValues(t);
+      this.control.linearRampToValueAtTime(valToSet, t);
+    }
+  };
+  /**
+   *  Trigger the Release of the Envelope. This is similar to releasing
+   *  the key on a piano and letting the sound fade according to the
+   *  release level and release time.
+   *
+   *  @method  triggerRelease
+   *  @param  {Object} unit p5.sound Object or Web Audio Param
+   *  @param  {Number} secondsFromNow time to trigger the release
+   *  @example
+   *  <div><code>
+   *  
+   *  var attackLevel = 1.0;
+   *  var releaseLevel = 0;
+   *  
+   *  var attackTime = 0.001
+   *  var decayTime = 0.3;
+   *  var susPercent = 0.4;
+   *  var releaseTime = 0.5;
+   *  
+   *  var env, triOsc;
+   *  
+   *  function setup() {
+   *    var cnv = createCanvas(100, 100);
+   *    background(200);
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *  
+   *    env = new p5.Env();
+   *    env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+   *    env.setRange(attackLevel, releaseLevel);
+   *  
+   *    triOsc = new p5.Oscillator('triangle');
+   *    triOsc.amp(env);
+   *    triOsc.start();
+   *    triOsc.freq(220);
+   *  
+   *    cnv.mousePressed(envAttack);
+   *  }
+   *  
+   *  function envAttack(){
+   *    console.log('trigger attack');
+   *    env.triggerAttack();
+   *  
+   *    background(0,255,0);
+   *    text('attack!', width/2, height/2);
+   *  }
+   *  
+   *  function mouseReleased() {
+   *    env.triggerRelease();
+   *  
+   *    background(200);
+   *    text('click to play', width/2, height/2);
+   *  }
+   *  </code></div>
+   */
+  p5.Env.prototype.triggerRelease = function (unit, secondsFromNow) {
+    // only trigger a release if an attack was triggered
+    if (!this.wasTriggered) {
+      // this currently causes a bit of trouble:
+      // if a later release has been scheduled (via the play function)
+      // a new earlier release won't interrupt it, because 
+      // this.wasTriggered has already been set to false.
+      // If we want new earlier releases to override, then we need to 
+      // keep track of the last release time, and if the new release time is 
+      // earlier, then use it.
+      return;
+    }
+    var now = p5sound.audiocontext.currentTime;
+    var tFromNow = secondsFromNow || 0;
+    var t = now + tFromNow;
+    if (unit) {
+      if (this.connection !== unit) {
+        this.connect(unit);
+      }
+    }
+    // get and set value (with linear or exponential ramp) to anchor automation
+    var valToSet = this.control.getValueAtTime(t);
+    this.control.cancelScheduledValues(t);
+    // not sure if this is necessary
+    if (this.isExponential == true) {
+      this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
+    } else {
+      this.control.linearRampToValueAtTime(valToSet, t);
+    }
+    // release
+    t += this.rTime;
+    if (this.isExponential == true) {
+      this.control.exponentialRampToValueAtTime(this.checkExpInput(this.rLevel), t);
+      valToSet = this.checkExpInput(this.control.getValueAtTime(t));
+      this.control.cancelScheduledValues(t);
+      this.control.exponentialRampToValueAtTime(valToSet, t);
+    } else {
+      this.control.linearRampToValueAtTime(this.rLevel, t);
+      valToSet = this.control.getValueAtTime(t);
+      this.control.cancelScheduledValues(t);
+      this.control.linearRampToValueAtTime(valToSet, t);
+    }
+    this.wasTriggered = false;
+  };
+  /**
+   *  Exponentially ramp to a value using the first two
+   *  values from <code><a href="#/p5.Env/setADSR">setADSR(attackTime, decayTime)</a></code>
+   *  as <a href="https://en.wikipedia.org/wiki/RC_time_constant">
+   *  time constants</a> for simple exponential ramps.
+   *  If the value is higher than current value, it uses attackTime,
+   *  while a decrease uses decayTime.
+   *
+   *  @method  ramp
+   *  @param  {Object} unit           p5.sound Object or Web Audio Param
+   *  @param  {Number} secondsFromNow When to trigger the ramp
+   *  @param  {Number} v              Target value
+   *  @param  {Number} [v2]           Second target value (optional)
+   *  @example
+   *  <div><code>
+   *  var env, osc, amp, cnv;
+   *  
+   *  var attackTime = 0.001;
+   *  var decayTime = 0.2;
+   *  var attackLevel = 1;
+   *  var decayLevel = 0;
+   *  
+   *  function setup() {
+   *    cnv = createCanvas(100, 100);
+   *    fill(0,255,0);
+   *    noStroke();
+   *  
+   *    env = new p5.Env();
+   *    env.setADSR(attackTime, decayTime);
+   *  
+   *    osc = new p5.Oscillator();
+   *    osc.amp(env);
+   *    osc.start();
+   *  
+   *    amp = new p5.Amplitude();
+   *  
+   *    cnv.mousePressed(triggerRamp);
+   *  }
+   *  
+   *  function triggerRamp() {
+   *    env.ramp(osc, 0, attackLevel, decayLevel);
+   *  }
+   *  
+   *  function draw() {
+   *    background(20,20,20);
+   *    text('click me', 10, 20);
+   *    var h = map(amp.getLevel(), 0, 0.4, 0, height);;
+   *  
+   *    rect(0, height, width, -h);
+   *  }
+   *  </code></div>
+   */
+  p5.Env.prototype.ramp = function (unit, secondsFromNow, v1, v2) {
+    var now = p5sound.audiocontext.currentTime;
+    var tFromNow = secondsFromNow || 0;
+    var t = now + tFromNow;
+    var destination1 = this.checkExpInput(v1);
+    var destination2 = typeof v2 !== 'undefined' ? this.checkExpInput(v2) : undefined;
+    // connect env to unit if not already connected
+    if (unit) {
+      if (this.connection !== unit) {
+        this.connect(unit);
+      }
+    }
+    //get current value
+    var currentVal = this.checkExpInput(this.control.getValueAtTime(t));
+    this.control.cancelScheduledValues(t);
+    //if it's going up
+    if (destination1 > currentVal) {
+      this.control.setTargetAtTime(destination1, t, this._rampAttackTC);
+      t += this._rampAttackTime;
+    } else if (destination1 < currentVal) {
+      this.control.setTargetAtTime(destination1, t, this._rampDecayTC);
+      t += this._rampDecayTime;
+    }
+    // Now the second part of envelope begins
+    if (destination2 === undefined)
+      return;
+    //if it's going up
+    if (destination2 > destination1) {
+      this.control.setTargetAtTime(destination2, t, this._rampAttackTC);
+    } else if (destination2 < destination1) {
+      this.control.setTargetAtTime(destination2, t, this._rampDecayTC);
+    }
+  };
+  p5.Env.prototype.connect = function (unit) {
+    this.connection = unit;
+    // assume we're talking about output gain
+    // unless given a different audio param
+    if (unit instanceof p5.Oscillator || unit instanceof p5.SoundFile || unit instanceof p5.AudioIn || unit instanceof p5.Reverb || unit instanceof p5.Noise || unit instanceof p5.Filter || unit instanceof p5.Delay) {
+      unit = unit.output.gain;
+    }
+    if (unit instanceof AudioParam) {
+      //set the initial value
+      unit.setValueAtTime(0, p5sound.audiocontext.currentTime);
+    }
+    if (unit instanceof p5.Signal) {
+      unit.setValue(0);
+    }
+    this.output.connect(unit);
+  };
+  p5.Env.prototype.disconnect = function (unit) {
+    this.output.disconnect();
+  };
+  // Signal Math
+  /**
+   *  Add a value to the p5.Oscillator's output amplitude,
+   *  and return the oscillator. Calling this method
+   *  again will override the initial add() with new values.
+   *  
+   *  @method  add
+   *  @param {Number} number Constant number to add
+   *  @return {p5.Env} Envelope Returns this envelope
+   *                                     with scaled output
+   */
+  p5.Env.prototype.add = function (num) {
+    var add = new Add(num);
+    var thisChain = this.mathOps.length;
+    var nextChain = this.output;
+    return p5.prototype._mathChain(this, add, thisChain, nextChain, Add);
+  };
+  /**
+   *  Multiply the p5.Env's output amplitude
+   *  by a fixed value. Calling this method
+   *  again will override the initial mult() with new values.
+   *  
+   *  @method  mult
+   *  @param {Number} number Constant number to multiply
+   *  @return {p5.Env} Envelope Returns this envelope
+   *                                     with scaled output
+   */
+  p5.Env.prototype.mult = function (num) {
+    var mult = new Mult(num);
+    var thisChain = this.mathOps.length;
+    var nextChain = this.output;
+    return p5.prototype._mathChain(this, mult, thisChain, nextChain, Mult);
+  };
+  /**
+   *  Scale this envelope's amplitude values to a given
+   *  range, and return the envelope. Calling this method
+   *  again will override the initial scale() with new values.
+   *  
+   *  @method  scale
+   *  @param  {Number} inMin  input range minumum
+   *  @param  {Number} inMax  input range maximum
+   *  @param  {Number} outMin input range minumum
+   *  @param  {Number} outMax input range maximum
+   *  @return {p5.Env} Envelope Returns this envelope
+   *                                     with scaled output
+   */
+  p5.Env.prototype.scale = function (inMin, inMax, outMin, outMax) {
+    var scale = new Scale(inMin, inMax, outMin, outMax);
+    var thisChain = this.mathOps.length;
+    var nextChain = this.output;
+    return p5.prototype._mathChain(this, scale, thisChain, nextChain, Scale);
+  };
+  // get rid of the oscillator
+  p5.Env.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    var now = p5sound.audiocontext.currentTime;
+    this.disconnect();
+    try {
+      this.control.dispose();
+      this.control = null;
+    } catch (e) {
+    }
+    for (var i = 1; i < this.mathOps.length; i++) {
+      mathOps[i].dispose();
+    }
+  };
+}(master, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_signal_TimelineSignal, Tone_core_Tone);
+var pulse;
+pulse = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+   *  Creates a Pulse object, an oscillator that implements
+   *  Pulse Width Modulation.
+   *  The pulse is created with two oscillators.
+   *  Accepts a parameter for frequency, and to set the
+   *  width between the pulses. See <a href="
+   *  http://p5js.org/reference/#/p5.Oscillator">
+   *  <code>p5.Oscillator</code> for a full list of methods.
+   *  
+   *  @class p5.Pulse
+   *  @constructor
+   *  @param {Number} [freq] Frequency in oscillations per second (Hz)
+   *  @param {Number} [w]    Width between the pulses (0 to 1.0,
+   *                         defaults to 0)
+   *  @example
+   *  <div><code>
+   *  var pulse;
+   *  function setup() {
+   *    background(0);
+   *    
+   *    // Create and start the pulse wave oscillator
+   *    pulse = new p5.Pulse();
+   *    pulse.amp(0.5);
+   *    pulse.freq(220);
+   *    pulse.start();
+   *  }
+   *
+   *  function draw() {
+   *    var w = map(mouseX, 0, width, 0, 1);
+   *    w = constrain(w, 0, 1);
+   *    pulse.width(w)
+   *  }
+   *  </code></div>
+   */
+  p5.Pulse = function (freq, w) {
+    p5.Oscillator.call(this, freq, 'sawtooth');
+    // width of PWM, should be betw 0 to 1.0
+    this.w = w || 0;
+    // create a second oscillator with inverse frequency
+    this.osc2 = new p5.SawOsc(freq);
+    // create a delay node
+    this.dNode = p5sound.audiocontext.createDelay();
+    // dc offset
+    this.dcOffset = createDCOffset();
+    this.dcGain = p5sound.audiocontext.createGain();
+    this.dcOffset.connect(this.dcGain);
+    this.dcGain.connect(this.output);
+    // set delay time based on PWM width
+    this.f = freq || 440;
+    var mW = this.w / this.oscillator.frequency.value;
+    this.dNode.delayTime.value = mW;
+    this.dcGain.gain.value = 1.7 * (0.5 - this.w);
+    // disconnect osc2 and connect it to delay, which is connected to output
+    this.osc2.disconnect();
+    this.osc2.panner.disconnect();
+    this.osc2.amp(-1);
+    // inverted amplitude
+    this.osc2.output.connect(this.dNode);
+    this.dNode.connect(this.output);
+    this.output.gain.value = 1;
+    this.output.connect(this.panner);
+  };
+  p5.Pulse.prototype = Object.create(p5.Oscillator.prototype);
+  /**
+   *  Set the width of a Pulse object (an oscillator that implements
+   *  Pulse Width Modulation).
+   *
+   *  @method  width
+   *  @param {Number} [width]    Width between the pulses (0 to 1.0,
+   *                         defaults to 0)
+   */
+  p5.Pulse.prototype.width = function (w) {
+    if (typeof w === 'number') {
+      if (w <= 1 && w >= 0) {
+        this.w = w;
+        // set delay time based on PWM width
+        // var mW = map(this.w, 0, 1.0, 0, 1/this.f);
+        var mW = this.w / this.oscillator.frequency.value;
+        this.dNode.delayTime.value = mW;
+      }
+      this.dcGain.gain.value = 1.7 * (0.5 - this.w);
+    } else {
+      w.connect(this.dNode.delayTime);
+      var sig = new p5.SignalAdd(-0.5);
+      sig.setInput(w);
+      sig = sig.mult(-1);
+      sig = sig.mult(1.7);
+      sig.connect(this.dcGain.gain);
+    }
+  };
+  p5.Pulse.prototype.start = function (f, time) {
+    var now = p5sound.audiocontext.currentTime;
+    var t = time || 0;
+    if (!this.started) {
+      var freq = f || this.f;
+      var type = this.oscillator.type;
+      this.oscillator = p5sound.audiocontext.createOscillator();
+      this.oscillator.frequency.setValueAtTime(freq, now);
+      this.oscillator.type = type;
+      this.oscillator.connect(this.output);
+      this.oscillator.start(t + now);
+      // set up osc2
+      this.osc2.oscillator = p5sound.audiocontext.createOscillator();
+      this.osc2.oscillator.frequency.setValueAtTime(freq, t + now);
+      this.osc2.oscillator.type = type;
+      this.osc2.oscillator.connect(this.osc2.output);
+      this.osc2.start(t + now);
+      this.freqNode = [
+        this.oscillator.frequency,
+        this.osc2.oscillator.frequency
+      ];
+      // start dcOffset, too
+      this.dcOffset = createDCOffset();
+      this.dcOffset.connect(this.dcGain);
+      this.dcOffset.start(t + now);
+      // if LFO connections depend on these oscillators
+      if (this.mods !== undefined && this.mods.frequency !== undefined) {
+        this.mods.frequency.connect(this.freqNode[0]);
+        this.mods.frequency.connect(this.freqNode[1]);
+      }
+      this.started = true;
+      this.osc2.started = true;
+    }
+  };
+  p5.Pulse.prototype.stop = function (time) {
+    if (this.started) {
+      var t = time || 0;
+      var now = p5sound.audiocontext.currentTime;
+      this.oscillator.stop(t + now);
+      this.osc2.oscillator.stop(t + now);
+      this.dcOffset.stop(t + now);
+      this.started = false;
+      this.osc2.started = false;
+    }
+  };
+  p5.Pulse.prototype.freq = function (val, rampTime, tFromNow) {
+    if (typeof val === 'number') {
+      this.f = val;
+      var now = p5sound.audiocontext.currentTime;
+      var rampTime = rampTime || 0;
+      var tFromNow = tFromNow || 0;
+      var currentFreq = this.oscillator.frequency.value;
+      this.oscillator.frequency.cancelScheduledValues(now);
+      this.oscillator.frequency.setValueAtTime(currentFreq, now + tFromNow);
+      this.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
+      this.osc2.oscillator.frequency.cancelScheduledValues(now);
+      this.osc2.oscillator.frequency.setValueAtTime(currentFreq, now + tFromNow);
+      this.osc2.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
+      if (this.freqMod) {
+        this.freqMod.output.disconnect();
+        this.freqMod = null;
+      }
+    } else if (val.output) {
+      val.output.disconnect();
+      val.output.connect(this.oscillator.frequency);
+      val.output.connect(this.osc2.oscillator.frequency);
+      this.freqMod = val;
+    }
+  };
+  // inspiration: http://webaudiodemos.appspot.com/oscilloscope/
+  function createDCOffset() {
+    var ac = p5sound.audiocontext;
+    var buffer = ac.createBuffer(1, 2048, ac.sampleRate);
+    var data = buffer.getChannelData(0);
+    for (var i = 0; i < 2048; i++)
+      data[i] = 1;
+    var bufferSource = ac.createBufferSource();
+    bufferSource.buffer = buffer;
+    bufferSource.loop = true;
+    return bufferSource;
+  }
+}(master, oscillator);
+var noise;
+noise = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+   *  Noise is a type of oscillator that generates a buffer with random values.
+   *
+   *  @class p5.Noise
+   *  @constructor
+   *  @param {String} type Type of noise can be 'white' (default),
+   *                       'brown' or 'pink'.
+   *  @return {Object}    Noise Object
+   */
+  p5.Noise = function (type) {
+    p5.Oscillator.call(this);
+    delete this.f;
+    delete this.freq;
+    delete this.oscillator;
+    this.buffer = _whiteNoise;
+  };
+  p5.Noise.prototype = Object.create(p5.Oscillator.prototype);
+  // generate noise buffers
+  var _whiteNoise = function () {
+    var bufferSize = 2 * p5sound.audiocontext.sampleRate;
+    var whiteBuffer = p5sound.audiocontext.createBuffer(1, bufferSize, p5sound.audiocontext.sampleRate);
+    var noiseData = whiteBuffer.getChannelData(0);
+    for (var i = 0; i < bufferSize; i++) {
+      noiseData[i] = Math.random() * 2 - 1;
+    }
+    whiteBuffer.type = 'white';
+    return whiteBuffer;
+  }();
+  var _pinkNoise = function () {
+    var bufferSize = 2 * p5sound.audiocontext.sampleRate;
+    var pinkBuffer = p5sound.audiocontext.createBuffer(1, bufferSize, p5sound.audiocontext.sampleRate);
+    var noiseData = pinkBuffer.getChannelData(0);
+    var b0, b1, b2, b3, b4, b5, b6;
+    b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0;
+    for (var i = 0; i < bufferSize; i++) {
+      var white = Math.random() * 2 - 1;
+      b0 = 0.99886 * b0 + white * 0.0555179;
+      b1 = 0.99332 * b1 + white * 0.0750759;
+      b2 = 0.969 * b2 + white * 0.153852;
+      b3 = 0.8665 * b3 + white * 0.3104856;
+      b4 = 0.55 * b4 + white * 0.5329522;
+      b5 = -0.7616 * b5 - white * 0.016898;
+      noiseData[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
+      noiseData[i] *= 0.11;
+      // (roughly) compensate for gain
+      b6 = white * 0.115926;
+    }
+    pinkBuffer.type = 'pink';
+    return pinkBuffer;
+  }();
+  var _brownNoise = function () {
+    var bufferSize = 2 * p5sound.audiocontext.sampleRate;
+    var brownBuffer = p5sound.audiocontext.createBuffer(1, bufferSize, p5sound.audiocontext.sampleRate);
+    var noiseData = brownBuffer.getChannelData(0);
+    var lastOut = 0;
+    for (var i = 0; i < bufferSize; i++) {
+      var white = Math.random() * 2 - 1;
+      noiseData[i] = (lastOut + 0.02 * white) / 1.02;
+      lastOut = noiseData[i];
+      noiseData[i] *= 3.5;
+    }
+    brownBuffer.type = 'brown';
+    return brownBuffer;
+  }();
+  /**
+   *  Set type of noise to 'white', 'pink' or 'brown'.
+   *  White is the default.
+   *
+   *  @method setType
+   *  @param {String} [type] 'white', 'pink' or 'brown'
+   */
+  p5.Noise.prototype.setType = function (type) {
+    switch (type) {
+    case 'white':
+      this.buffer = _whiteNoise;
+      break;
+    case 'pink':
+      this.buffer = _pinkNoise;
+      break;
+    case 'brown':
+      this.buffer = _brownNoise;
+      break;
+    default:
+      this.buffer = _whiteNoise;
+    }
+    if (this.started) {
+      var now = p5sound.audiocontext.currentTime;
+      this.stop(now);
+      this.start(now + 0.01);
+    }
+  };
+  p5.Noise.prototype.getType = function () {
+    return this.buffer.type;
+  };
+  /**
+   *  Start the noise
+   *
+   *  @method start
+   */
+  p5.Noise.prototype.start = function () {
+    if (this.started) {
+      this.stop();
+    }
+    this.noise = p5sound.audiocontext.createBufferSource();
+    this.noise.buffer = this.buffer;
+    this.noise.loop = true;
+    this.noise.connect(this.output);
+    var now = p5sound.audiocontext.currentTime;
+    this.noise.start(now);
+    this.started = true;
+  };
+  /**
+   *  Stop the noise.
+   *
+   *  @method  stop
+   */
+  p5.Noise.prototype.stop = function () {
+    var now = p5sound.audiocontext.currentTime;
+    if (this.noise) {
+      this.noise.stop(now);
+      this.started = false;
+    }
+  };
+  /**
+   *  Pan the noise.
+   *
+   *  @method  pan
+   *  @param  {Number} panning Number between -1 (left)
+   *                           and 1 (right)
+   *  @param  {Number} timeFromNow schedule this event to happen
+   *                                seconds from now
+   */
+  /**
+   *  Set the amplitude of the noise between 0 and 1.0. Or,
+   *  modulate amplitude with an audio signal such as an oscillator.
+   *  
+   *  @param  {Number|Object} volume amplitude between 0 and 1.0
+   *                                     or modulating signal/oscillator
+   *  @param {Number} [rampTime] create a fade that lasts rampTime 
+   *  @param {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   */
+  /**
+   *  Send output to a p5.sound or web audio object
+   *  
+   *  @method  connect
+   *  @param  {Object} unit
+   */
+  /**
+   *  Disconnect all output.
+   *  
+   *  @method disconnect
+   */
+  p5.Noise.prototype.dispose = function () {
+    var now = p5sound.audiocontext.currentTime;
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    if (this.noise) {
+      this.noise.disconnect();
+      this.stop(now);
+    }
+    if (this.output) {
+      this.output.disconnect();
+    }
+    if (this.panner) {
+      this.panner.disconnect();
+    }
+    this.output = null;
+    this.panner = null;
+    this.buffer = null;
+    this.noise = null;
+  };
+}(master);
+var audioin;
+audioin = function () {
+  'use strict';
+  var p5sound = master;
+  var CustomError = errorHandler;
+  /**
+   *  <p>Get audio from an input, i.e. your computer's microphone.</p>
+   *
+   *  <p>Turn the mic on/off with the start() and stop() methods. When the mic
+   *  is on, its volume can be measured with getLevel or by connecting an
+   *  FFT object.</p>
+   *  
+   *  <p>If you want to hear the AudioIn, use the .connect() method. 
+   *  AudioIn does not connect to p5.sound output by default to prevent
+   *  feedback.</p> 
+   *
+   *  <p><em>Note: This uses the <a href="http://caniuse.com/stream">getUserMedia/
+   *  Stream</a> API, which is not supported by certain browsers.</em></p>
+   *
+   *  @class p5.AudioIn
+   *  @constructor
+   *  @return {Object} AudioIn
+   *  @example
+   *  <div><code>
+   *  var mic;
+   *  function setup(){
+   *    mic = new p5.AudioIn()
+   *    mic.start();
+   *  }
+   *  function draw(){
+   *    background(0);
+   *    micLevel = mic.getLevel();
+   *    ellipse(width/2, constrain(height-micLevel*height*5, 0, height), 10, 10);
+   *  }
+   *  </code></div>
+   */
+  p5.AudioIn = function () {
+    // set up audio input
+    this.input = p5sound.audiocontext.createGain();
+    this.output = p5sound.audiocontext.createGain();
+    this.stream = null;
+    this.mediaStream = null;
+    this.currentSource = 0;
+    /**
+     *  Client must allow browser to access their microphone / audioin source.
+     *  Default: false. Will become true when the client enables acces.
+     *
+     *  @property {Boolean} enabled
+     */
+    this.enabled = false;
+    // create an amplitude, connect to it by default but not to master out
+    this.amplitude = new p5.Amplitude();
+    this.output.connect(this.amplitude.input);
+    // Some browsers let developer determine their input sources
+    if (typeof window.MediaStreamTrack === 'undefined') {
+      window.alert('This browser does not support MediaStreamTrack');
+    } else if (typeof window.MediaStreamTrack.getSources === 'function') {
+      // Chrome supports getSources to list inputs. Dev picks default
+      window.MediaStreamTrack.getSources(this._gotSources);
+    } else {
+    }
+    // add to soundArray so we can dispose on close
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Start processing audio input. This enables the use of other
+   *  AudioIn methods like getLevel(). Note that by default, AudioIn
+   *  is not connected to p5.sound's output. So you won't hear
+   *  anything unless you use the connect() method.<br/>
+   *
+   *  @method start
+   *  @param {Function} successCallback Name of a function to call on
+   *                                    success.
+   *  @param {Function} errorCallback Name of a function to call if
+   *                                    there was an error. For example,
+   *                                    some browsers do not support
+   *                                    getUserMedia.
+   */
+  p5.AudioIn.prototype.start = function (successCallback, errorCallback) {
+    var self = this;
+    // if _gotSources() i.e. developers determine which source to use
+    if (p5sound.inputSources[self.currentSource]) {
+      // set the audio source
+      var audioSource = p5sound.inputSources[self.currentSource].id;
+      var constraints = { audio: { optional: [{ sourceId: audioSource }] } };
+      window.navigator.getUserMedia(constraints, this._onStream = function (stream) {
+        self.stream = stream;
+        self.enabled = true;
+        // Wrap a MediaStreamSourceNode around the live input
+        self.mediaStream = p5sound.audiocontext.createMediaStreamSource(stream);
+        self.mediaStream.connect(self.output);
+        if (successCallback)
+          successCallback();
+        // only send to the Amplitude reader, so we can see it but not hear it.
+        self.amplitude.setInput(self.output);
+      }, this._onStreamError = function (e) {
+        if (errorCallback)
+          errorCallback(e);
+        else
+          console.error(e);
+      });
+    } else {
+      // if Firefox where users select their source via browser
+      // if (typeof MediaStreamTrack.getSources === 'undefined') {
+      // Only get the audio stream.
+      window.navigator.getUserMedia({ 'audio': true }, this._onStream = function (stream) {
+        self.stream = stream;
+        self.enabled = true;
+        // Wrap a MediaStreamSourceNode around the live input
+        self.mediaStream = p5sound.audiocontext.createMediaStreamSource(stream);
+        self.mediaStream.connect(self.output);
+        // only send to the Amplitude reader, so we can see it but not hear it.
+        self.amplitude.setInput(self.output);
+        if (successCallback)
+          successCallback();
+      }, this._onStreamError = function (e) {
+        if (errorCallback)
+          errorCallback(e);
+        else
+          console.error(e);
+      });
+    }
+  };
+  /**
+   *  Turn the AudioIn off. If the AudioIn is stopped, it cannot getLevel().<br/>
+   *
+   *  @method stop
+   */
+  p5.AudioIn.prototype.stop = function () {
+    if (this.stream) {
+      this.stream.stop();
+    }
+  };
+  /**
+   *  Connect to an audio unit. If no parameter is provided, will
+   *  connect to the master output (i.e. your speakers).<br/>
+   *  
+   *  @method  connect
+   *  @param  {Object} [unit] An object that accepts audio input,
+   *                          such as an FFT
+   */
+  p5.AudioIn.prototype.connect = function (unit) {
+    if (unit) {
+      if (unit.hasOwnProperty('input')) {
+        this.output.connect(unit.input);
+      } else if (unit.hasOwnProperty('analyser')) {
+        this.output.connect(unit.analyser);
+      } else {
+        this.output.connect(unit);
+      }
+    } else {
+      this.output.connect(p5sound.input);
+    }
+  };
+  /**
+   *  Disconnect the AudioIn from all audio units. For example, if
+   *  connect() had been called, disconnect() will stop sending 
+   *  signal to your speakers.<br/>
+   *
+   *  @method  disconnect
+   */
+  p5.AudioIn.prototype.disconnect = function (unit) {
+    this.output.disconnect(unit);
+    // stay connected to amplitude even if not outputting to p5
+    this.output.connect(this.amplitude.input);
+  };
+  /**
+   *  Read the Amplitude (volume level) of an AudioIn. The AudioIn
+   *  class contains its own instance of the Amplitude class to help
+   *  make it easy to get a microphone's volume level. Accepts an
+   *  optional smoothing value (0.0 < 1.0). <em>NOTE: AudioIn must
+   *  .start() before using .getLevel().</em><br/>
+   *  
+   *  @method  getLevel
+   *  @param  {Number} [smoothing] Smoothing is 0.0 by default.
+   *                               Smooths values based on previous values.
+   *  @return {Number}           Volume level (between 0.0 and 1.0)
+   */
+  p5.AudioIn.prototype.getLevel = function (smoothing) {
+    if (smoothing) {
+      this.amplitude.smoothing = smoothing;
+    }
+    return this.amplitude.getLevel();
+  };
+  /**
+   *  Add input sources to the list of available sources.
+   *  
+   *  @private
+   */
+  p5.AudioIn.prototype._gotSources = function (sourceInfos) {
+    for (var i = 0; i < sourceInfos.length; i++) {
+      var sourceInfo = sourceInfos[i];
+      if (sourceInfo.kind === 'audio') {
+        // add the inputs to inputSources
+        //p5sound.inputSources.push(sourceInfo);
+        return sourceInfo;
+      }
+    }
+  };
+  /**
+   *  Set amplitude (volume) of a mic input between 0 and 1.0. <br/>
+   *
+   *  @method  amp
+   *  @param  {Number} vol between 0 and 1.0
+   *  @param {Number} [time] ramp time (optional)
+   */
+  p5.AudioIn.prototype.amp = function (vol, t) {
+    if (t) {
+      var rampTime = t || 0;
+      var currentVol = this.output.gain.value;
+      this.output.gain.cancelScheduledValues(p5sound.audiocontext.currentTime);
+      this.output.gain.setValueAtTime(currentVol, p5sound.audiocontext.currentTime);
+      this.output.gain.linearRampToValueAtTime(vol, rampTime + p5sound.audiocontext.currentTime);
+    } else {
+      this.output.gain.cancelScheduledValues(p5sound.audiocontext.currentTime);
+      this.output.gain.setValueAtTime(vol, p5sound.audiocontext.currentTime);
+    }
+  };
+  p5.AudioIn.prototype.listSources = function () {
+    console.log('listSources is deprecated - please use AudioIn.getSources');
+    console.log('input sources: ');
+    if (p5sound.inputSources.length > 0) {
+      return p5sound.inputSources;
+    } else {
+      return 'This browser does not support MediaStreamTrack.getSources()';
+    }
+  };
+  /**
+   * Chrome only. Returns a list of available input sources 
+   * and allows the user to set the media source. Firefox allows 
+   * the user to choose from input sources in the permissions dialogue
+   * instead of enumerating available sources and selecting one.
+   * Note: in order to have descriptive media names your page must be 
+   * served over a secure (HTTPS) connection and the page should 
+   * request user media before enumerating devices. Otherwise device 
+   * ID will be a long device ID number and does not specify device 
+   * type. For example see 
+   * https://simpl.info/getusermedia/sources/index.html vs.
+   * http://simpl.info/getusermedia/sources/index.html
+   *
+   * @method  getSources
+   * @param  {Function} callback a callback to handle the sources 
+   *                               when they have been enumerated
+   * @example
+   *  <div><code>
+   *  var audiograb;
+   *      
+   *  function setup(){
+   *    //new audioIn
+   *    audioGrab = new p5.AudioIn();
+   *    
+   *    audioGrab.getSources(function(sourceList) {
+   *      //print out the array of available sources
+   *      console.log(sourceList);
+   *      //set the source to the first item in the inputSources array
+   *      audioGrab.setSource(0);
+   *    });
+   *  }
+   *  </code></div>
+   */
+  p5.AudioIn.prototype.getSources = function (callback) {
+    if (typeof window.MediaStreamTrack.getSources === 'function') {
+      window.MediaStreamTrack.getSources(function (data) {
+        for (var i = 0, max = data.length; i < max; i++) {
+          var sourceInfo = data[i];
+          if (sourceInfo.kind === 'audio') {
+            // add the inputs to inputSources
+            p5sound.inputSources.push(sourceInfo);
+          }
+        }
+        callback(p5sound.inputSources);
+      });
+    } else {
+      console.log('This browser does not support MediaStreamTrack.getSources()');
+    }
+  };
+  /**
+   *  Set the input source. Accepts a number representing a
+   *  position in the array returned by listSources().
+   *  This is only available in browsers that support 
+   *  MediaStreamTrack.getSources(). Instead, some browsers
+   *  give users the option to set their own media source.<br/>
+   *  
+   *  @method setSource
+   *  @param {number} num position of input source in the array
+   */
+  p5.AudioIn.prototype.setSource = function (num) {
+    // TO DO - set input by string or # (array position)
+    var self = this;
+    if (p5sound.inputSources.length > 0 && num < p5sound.inputSources.length) {
+      // set the current source
+      self.currentSource = num;
+      console.log('set source to ' + p5sound.inputSources[self.currentSource].id);
+    } else {
+      console.log('unable to set input source');
+    }
+  };
+  // private method
+  p5.AudioIn.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this.stop();
+    if (this.output) {
+      this.output.disconnect();
+    }
+    if (this.amplitude) {
+      this.amplitude.disconnect();
+    }
+    this.amplitude = null;
+    this.output = null;
+  };
+}(master, errorHandler);
+var filter;
+filter = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+   *  A p5.Filter uses a Web Audio Biquad Filter to filter
+   *  the frequency response of an input source. Inheriting
+   *  classes include:<br/>
+   *  * <code>p5.LowPass</code> - allows frequencies below
+   *  the cutoff frequency to pass through, and attenuates
+   *  frequencies above the cutoff.<br/>
+   *  * <code>p5.HighPass</code> - the opposite of a lowpass
+   *  filter. <br/>
+   *  * <code>p5.BandPass</code> -  allows a range of
+   *  frequencies to pass through and attenuates the frequencies
+   *  below and above this frequency range.<br/>
+   *
+   *  The <code>.res()</code> method controls either width of the
+   *  bandpass, or resonance of the low/highpass cutoff frequency.
+   *
+   *  @class p5.Filter
+   *  @constructor
+   *  @param {[String]} type 'lowpass' (default), 'highpass', 'bandpass'
+   *  @return {Object} p5.Filter
+   *  @example
+   *  <div><code>
+   *  var fft, noise, filter;
+   *
+   *  function setup() {
+   *    fill(255, 40, 255);
+   *
+   *    filter = new p5.BandPass();
+   *
+   *    noise = new p5.Noise();
+   *    // disconnect unfiltered noise,
+   *    // and connect to filter
+   *    noise.disconnect();
+   *    noise.connect(filter);
+   *    noise.start();
+   *
+   *    fft = new p5.FFT();
+   *  }
+   *
+   *  function draw() {
+   *    background(30);
+   *
+   *    // set the BandPass frequency based on mouseX
+   *    var freq = map(mouseX, 0, width, 20, 10000);
+   *    filter.freq(freq);
+   *    // give the filter a narrow band (lower res = wider bandpass)
+   *    filter.res(50);
+   *
+   *    // draw filtered spectrum
+   *    var spectrum = fft.analyze();
+   *    noStroke();
+   *    for (var i = 0; i < spectrum.length; i++) {
+   *      var x = map(i, 0, spectrum.length, 0, width);
+   *      var h = -height + map(spectrum[i], 0, 255, height, 0);
+   *      rect(x, height, width/spectrum.length, h);
+   *    }
+   *    
+   *    isMouseOverCanvas();
+   *  }
+   *
+   *  function isMouseOverCanvas() {
+   *    var mX = mouseX, mY = mouseY;
+   *    if (mX > 0 && mX < width && mY < height && mY > 0) {
+   *      noise.amp(0.5, 0.2);
+   *    } else {
+   *      noise.amp(0, 0.2);
+   *    }
+   *  }
+   *  </code></div>
+   */
+  p5.Filter = function (type) {
+    this.ac = p5sound.audiocontext;
+    this.input = this.ac.createGain();
+    this.output = this.ac.createGain();
+    /**
+     *  The p5.Filter is built with a
+     *  <a href="http://www.w3.org/TR/webaudio/#BiquadFilterNode">
+     *  Web Audio BiquadFilter Node</a>.
+     *  
+     *  @property biquadFilter
+     *  @type {Object}  Web Audio Delay Node
+     */
+    this.biquad = this.ac.createBiquadFilter();
+    this.input.connect(this.biquad);
+    this.biquad.connect(this.output);
+    this.connect();
+    if (type) {
+      this.setType(type);
+    }
+    // add to the soundArray
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Filter an audio signal according to a set
+   *  of filter parameters.
+   *  
+   *  @method  process
+   *  @param  {Object} Signal  An object that outputs audio
+   *  @param {[Number]} freq Frequency in Hz, from 10 to 22050
+   *  @param {[Number]} res Resonance/Width of the filter frequency
+   *                        from 0.001 to 1000
+   */
+  p5.Filter.prototype.process = function (src, freq, res) {
+    src.connect(this.input);
+    this.set(freq, res);
+  };
+  /**
+   *  Set the frequency and the resonance of the filter.
+   *
+   *  @method  set
+   *  @param {Number} freq Frequency in Hz, from 10 to 22050
+   *  @param {Number} res  Resonance (Q) from 0.001 to 1000
+   *  @param {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   */
+  p5.Filter.prototype.set = function (freq, res, time) {
+    if (freq) {
+      this.freq(freq, time);
+    }
+    if (res) {
+      this.res(res, time);
+    }
+  };
+  /**
+   *  Set the filter frequency, in Hz, from 10 to 22050 (the range of
+   *  human hearing, although in reality most people hear in a narrower
+   *  range).
+   *
+   *  @method  freq
+   *  @param  {Number} freq Filter Frequency
+   *  @param {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   *  @return {Number} value  Returns the current frequency value
+   */
+  p5.Filter.prototype.freq = function (freq, time) {
+    var self = this;
+    var t = time || 0;
+    if (freq <= 0) {
+      freq = 1;
+    }
+    if (typeof freq === 'number') {
+      self.biquad.frequency.value = freq;
+      self.biquad.frequency.cancelScheduledValues(this.ac.currentTime + 0.01 + t);
+      self.biquad.frequency.exponentialRampToValueAtTime(freq, this.ac.currentTime + 0.02 + t);
+    } else if (freq) {
+      freq.connect(this.biquad.frequency);
+    }
+    return self.biquad.frequency.value;
+  };
+  /**
+   *  Controls either width of a bandpass frequency,
+   *  or the resonance of a low/highpass cutoff frequency.
+   *
+   *  @method  res
+   *  @param {Number} res  Resonance/Width of filter freq
+   *                       from 0.001 to 1000
+   *  @param {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   *  @return {Number} value Returns the current res value
+   */
+  p5.Filter.prototype.res = function (res, time) {
+    var self = this;
+    var t = time || 0;
+    if (typeof res == 'number') {
+      self.biquad.Q.value = res;
+      self.biquad.Q.cancelScheduledValues(self.ac.currentTime + 0.01 + t);
+      self.biquad.Q.linearRampToValueAtTime(res, self.ac.currentTime + 0.02 + t);
+    } else if (res) {
+      freq.connect(this.biquad.Q);
+    }
+    return self.biquad.Q.value;
+  };
+  /**
+   *  Set the type of a p5.Filter. Possible types include: 
+   *  "lowpass" (default), "highpass", "bandpass", 
+   *  "lowshelf", "highshelf", "peaking", "notch",
+   *  "allpass". 
+   *  
+   *  @method  setType
+   *  @param {String}
+   */
+  p5.Filter.prototype.setType = function (t) {
+    this.biquad.type = t;
+  };
+  /**
+   *  Set the output level of the filter.
+   *  
+   *  @method  amp
+   *  @param {Number} volume amplitude between 0 and 1.0
+   *  @param {Number} [rampTime] create a fade that lasts rampTime 
+   *  @param {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   */
+  p5.Filter.prototype.amp = function (vol, rampTime, tFromNow) {
+    var rampTime = rampTime || 0;
+    var tFromNow = tFromNow || 0;
+    var now = p5sound.audiocontext.currentTime;
+    var currentVol = this.output.gain.value;
+    this.output.gain.cancelScheduledValues(now);
+    this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
+    this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
+  };
+  /**
+   *  Send output to a p5.sound or web audio object
+   *  
+   *  @method connect
+   *  @param  {Object} unit
+   */
+  p5.Filter.prototype.connect = function (unit) {
+    var u = unit || p5.soundOut.input;
+    this.output.connect(u);
+  };
+  /**
+   *  Disconnect all output.
+   *  
+   *  @method disconnect
+   */
+  p5.Filter.prototype.disconnect = function () {
+    this.output.disconnect();
+  };
+  p5.Filter.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this.input.disconnect();
+    this.input = undefined;
+    this.output.disconnect();
+    this.output = undefined;
+    this.biquad.disconnect();
+    this.biquad = undefined;
+  };
+  /**
+   *  Constructor: <code>new p5.LowPass()</code> Filter.
+   *  This is the same as creating a p5.Filter and then calling
+   *  its method <code>setType('lowpass')</code>.
+   *  See p5.Filter for methods.
+   *  
+   *  @method p5.LowPass
+   */
+  p5.LowPass = function () {
+    p5.Filter.call(this, 'lowpass');
+  };
+  p5.LowPass.prototype = Object.create(p5.Filter.prototype);
+  /**
+   *  Constructor: <code>new p5.HighPass()</code> Filter.
+   *  This is the same as creating a p5.Filter and then calling
+   *  its method <code>setType('highpass')</code>.
+   *  See p5.Filter for methods.
+   *  
+   *  @method p5.HighPass
+   */
+  p5.HighPass = function () {
+    p5.Filter.call(this, 'highpass');
+  };
+  p5.HighPass.prototype = Object.create(p5.Filter.prototype);
+  /**
+   *  Constructor: <code>new p5.BandPass()</code> Filter.
+   *  This is the same as creating a p5.Filter and then calling
+   *  its method <code>setType('bandpass')</code>.
+   *  See p5.Filter for methods. 
+   *  
+   *  @method p5.BandPass
+   */
+  p5.BandPass = function () {
+    p5.Filter.call(this, 'bandpass');
+  };
+  p5.BandPass.prototype = Object.create(p5.Filter.prototype);
+}(master);
+var delay;
+delay = function () {
+  'use strict';
+  var p5sound = master;
+  var Filter = filter;
+  /**
+   *  Delay is an echo effect. It processes an existing sound source,
+   *  and outputs a delayed version of that sound. The p5.Delay can
+   *  produce different effects depending on the delayTime, feedback,
+   *  filter, and type. In the example below, a feedback of 0.5 will
+   *  produce a looping delay that decreases in volume by
+   *  50% each repeat. A filter will cut out the high frequencies so
+   *  that the delay does not sound as piercing as the original source.
+   *  
+   *  @class p5.Delay
+   *  @constructor
+   *  @return {Object} Returns a p5.Delay object
+   *  @example
+   *  <div><code>
+   *  var noise, env, delay;
+   *  
+   *  function setup() {
+   *    background(0);
+   *    noStroke();
+   *    fill(255);
+   *    textAlign(CENTER);
+   *    text('click to play', width/2, height/2);
+   *    
+   *    noise = new p5.Noise('brown');
+   *    noise.amp(0);
+   *    noise.start();
+   *    
+   *    delay = new p5.Delay();
+   *
+   *    // delay.process() accepts 4 parameters:
+   *    // source, delayTime, feedback, filter frequency
+   *    // play with these numbers!!
+   *    delay.process(noise, .12, .7, 2300);
+   *    
+   *    // play the noise with an envelope,
+   *    // a series of fades ( time / value pairs )
+   *    env = new p5.Env(.01, 0.2, .2, .1);
+   *  }
+   *
+   *  // mouseClick triggers envelope
+   *  function mouseClicked() {
+   *    // is mouse over canvas?
+   *    if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+   *      env.play(noise);
+   *    }
+   *  }
+   *  </code></div>
+   */
+  p5.Delay = function () {
+    this.ac = p5sound.audiocontext;
+    this.input = this.ac.createGain();
+    this.output = this.ac.createGain();
+    this._split = this.ac.createChannelSplitter(2);
+    this._merge = this.ac.createChannelMerger(2);
+    this._leftGain = this.ac.createGain();
+    this._rightGain = this.ac.createGain();
+    /**
+     *  The p5.Delay is built with two
+     *  <a href="http://www.w3.org/TR/webaudio/#DelayNode">
+     *  Web Audio Delay Nodes</a>, one for each stereo channel.
+     *  
+     *  @property leftDelay
+     *  @type {Object}  Web Audio Delay Node
+     */
+    this.leftDelay = this.ac.createDelay();
+    /**
+     *  The p5.Delay is built with two
+     *  <a href="http://www.w3.org/TR/webaudio/#DelayNode">
+     *  Web Audio Delay Nodes</a>, one for each stereo channel.
+     *  
+     *  @property rightDelay
+     *  @type {Object}  Web Audio Delay Node
+     */
+    this.rightDelay = this.ac.createDelay();
+    this._leftFilter = new p5.Filter();
+    this._rightFilter = new p5.Filter();
+    this._leftFilter.disconnect();
+    this._rightFilter.disconnect();
+    this._leftFilter.biquad.frequency.setValueAtTime(1200, this.ac.currentTime);
+    this._rightFilter.biquad.frequency.setValueAtTime(1200, this.ac.currentTime);
+    this._leftFilter.biquad.Q.setValueAtTime(0.3, this.ac.currentTime);
+    this._rightFilter.biquad.Q.setValueAtTime(0.3, this.ac.currentTime);
+    // graph routing
+    this.input.connect(this._split);
+    this.leftDelay.connect(this._leftGain);
+    this.rightDelay.connect(this._rightGain);
+    this._leftGain.connect(this._leftFilter.input);
+    this._rightGain.connect(this._rightFilter.input);
+    this._merge.connect(this.output);
+    this.output.connect(p5.soundOut.input);
+    this._leftFilter.biquad.gain.setValueAtTime(1, this.ac.currentTime);
+    this._rightFilter.biquad.gain.setValueAtTime(1, this.ac.currentTime);
+    // default routing
+    this.setType(0);
+    this._maxDelay = this.leftDelay.delayTime.maxValue;
+    // add this p5.SoundFile to the soundArray
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Add delay to an audio signal according to a set
+   *  of delay parameters.
+   *  
+   *  @method  process
+   *  @param  {Object} Signal  An object that outputs audio
+   *  @param  {Number} [delayTime] Time (in seconds) of the delay/echo.
+   *                               Some browsers limit delayTime to
+   *                               1 second.
+   *  @param  {Number} [feedback]  sends the delay back through itself
+   *                               in a loop that decreases in volume
+   *                               each time.
+   *  @param  {Number} [lowPass]   Cutoff frequency. Only frequencies
+   *                               below the lowPass will be part of the
+   *                               delay.
+   */
+  p5.Delay.prototype.process = function (src, _delayTime, _feedback, _filter) {
+    var feedback = _feedback || 0;
+    var delayTime = _delayTime || 0;
+    if (feedback >= 1) {
+      throw new Error('Feedback value will force a positive feedback loop.');
+    }
+    if (delayTime >= this._maxDelay) {
+      throw new Error('Delay Time exceeds maximum delay time of ' + this._maxDelay + ' second.');
+    }
+    src.connect(this.input);
+    this.leftDelay.delayTime.setValueAtTime(delayTime, this.ac.currentTime);
+    this.rightDelay.delayTime.setValueAtTime(delayTime, this.ac.currentTime);
+    this._leftGain.gain.setValueAtTime(feedback, this.ac.currentTime);
+    this._rightGain.gain.setValueAtTime(feedback, this.ac.currentTime);
+    if (_filter) {
+      this._leftFilter.freq(_filter);
+      this._rightFilter.freq(_filter);
+    }
+  };
+  /**
+   *  Set the delay (echo) time, in seconds. Usually this value will be
+   *  a floating point number between 0.0 and 1.0.
+   *
+   *  @method  delayTime
+   *  @param {Number} delayTime Time (in seconds) of the delay
+   */
+  p5.Delay.prototype.delayTime = function (t) {
+    // if t is an audio node...
+    if (typeof t !== 'number') {
+      t.connect(this.leftDelay.delayTime);
+      t.connect(this.rightDelay.delayTime);
+    } else {
+      this.leftDelay.delayTime.cancelScheduledValues(this.ac.currentTime);
+      this.rightDelay.delayTime.cancelScheduledValues(this.ac.currentTime);
+      this.leftDelay.delayTime.linearRampToValueAtTime(t, this.ac.currentTime);
+      this.rightDelay.delayTime.linearRampToValueAtTime(t, this.ac.currentTime);
+    }
+  };
+  /**
+   *  Feedback occurs when Delay sends its signal back through its input
+   *  in a loop. The feedback amount determines how much signal to send each
+   *  time through the loop. A feedback greater than 1.0 is not desirable because
+   *  it will increase the overall output each time through the loop,
+   *  creating an infinite feedback loop.
+   *  
+   *  @method  feedback
+   *  @param {Number|Object} feedback 0.0 to 1.0, or an object such as an
+   *                                  Oscillator that can be used to
+   *                                  modulate this param
+   */
+  p5.Delay.prototype.feedback = function (f) {
+    // if f is an audio node...
+    if (typeof f !== 'number') {
+      f.connect(this._leftGain.gain);
+      f.connect(this._rightGain.gain);
+    } else if (f >= 1) {
+      throw new Error('Feedback value will force a positive feedback loop.');
+    } else {
+      this._leftGain.gain.exponentialRampToValueAtTime(f, this.ac.currentTime);
+      this._rightGain.gain.exponentialRampToValueAtTime(f, this.ac.currentTime);
+    }
+  };
+  /**
+   *  Set a lowpass filter frequency for the delay. A lowpass filter
+   *  will cut off any frequencies higher than the filter frequency.
+   *   
+   *  @method  filter
+   *  @param {Number|Object} cutoffFreq  A lowpass filter will cut off any 
+   *                              frequencies higher than the filter frequency.
+   *  @param {Number|Object} res  Resonance of the filter frequency
+   *                              cutoff, or an object (i.e. a p5.Oscillator)
+   *                              that can be used to modulate this parameter.
+   *                              High numbers (i.e. 15) will produce a resonance,
+   *                              low numbers (i.e. .2) will produce a slope.
+   */
+  p5.Delay.prototype.filter = function (freq, q) {
+    this._leftFilter.set(freq, q);
+    this._rightFilter.set(freq, q);
+  };
+  /**
+   *  Choose a preset type of delay. 'pingPong' bounces the signal
+   *  from the left to the right channel to produce a stereo effect.
+   *  Any other parameter will revert to the default delay setting.
+   *  
+   *  @method  setType
+   *  @param {String|Number} type 'pingPong' (1) or 'default' (0)
+   */
+  p5.Delay.prototype.setType = function (t) {
+    if (t === 1) {
+      t = 'pingPong';
+    }
+    this._split.disconnect();
+    this._leftFilter.disconnect();
+    this._rightFilter.disconnect();
+    this._split.connect(this.leftDelay, 0);
+    this._split.connect(this.rightDelay, 1);
+    switch (t) {
+    case 'pingPong':
+      this._rightFilter.setType(this._leftFilter.biquad.type);
+      this._leftFilter.output.connect(this._merge, 0, 0);
+      this._rightFilter.output.connect(this._merge, 0, 1);
+      this._leftFilter.output.connect(this.rightDelay);
+      this._rightFilter.output.connect(this.leftDelay);
+      break;
+    default:
+      this._leftFilter.output.connect(this._merge, 0, 0);
+      this._leftFilter.output.connect(this._merge, 0, 1);
+      this._leftFilter.output.connect(this.leftDelay);
+      this._leftFilter.output.connect(this.rightDelay);
+    }
+  };
+  /**
+   *  Set the output level of the delay effect.
+   *  
+   *  @method  amp
+   *  @param  {Number} volume amplitude between 0 and 1.0
+   *  @param {Number} [rampTime] create a fade that lasts rampTime 
+   *  @param {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   */
+  p5.Delay.prototype.amp = function (vol, rampTime, tFromNow) {
+    var rampTime = rampTime || 0;
+    var tFromNow = tFromNow || 0;
+    var now = p5sound.audiocontext.currentTime;
+    var currentVol = this.output.gain.value;
+    this.output.gain.cancelScheduledValues(now);
+    this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
+    this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
+  };
+  /**
+   *  Send output to a p5.sound or web audio object
+   *  
+   *  @method  connect
+   *  @param  {Object} unit
+   */
+  p5.Delay.prototype.connect = function (unit) {
+    var u = unit || p5.soundOut.input;
+    this.output.connect(u);
+  };
+  /**
+   *  Disconnect all output.
+   *  
+   *  @method disconnect
+   */
+  p5.Delay.prototype.disconnect = function () {
+    this.output.disconnect();
+  };
+  p5.Delay.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this.input.disconnect();
+    this.output.disconnect();
+    this._split.disconnect();
+    this._leftFilter.disconnect();
+    this._rightFilter.disconnect();
+    this._merge.disconnect();
+    this._leftGain.disconnect();
+    this._rightGain.disconnect();
+    this.leftDelay.disconnect();
+    this.rightDelay.disconnect();
+    this.input = undefined;
+    this.output = undefined;
+    this._split = undefined;
+    this._leftFilter = undefined;
+    this._rightFilter = undefined;
+    this._merge = undefined;
+    this._leftGain = undefined;
+    this._rightGain = undefined;
+    this.leftDelay = undefined;
+    this.rightDelay = undefined;
+  };
+}(master, filter);
+var reverb;
+reverb = function () {
+  'use strict';
+  var p5sound = master;
+  var CustomError = errorHandler;
+  /**
+   *  Reverb adds depth to a sound through a large number of decaying
+   *  echoes. It creates the perception that sound is occurring in a
+   *  physical space. The p5.Reverb has paramters for Time (how long does the
+   *  reverb last) and decayRate (how much the sound decays with each echo)
+   *  that can be set with the .set() or .process() methods. The p5.Convolver
+   *  extends p5.Reverb allowing you to recreate the sound of actual physical
+   *  spaces through convolution.
+   *  
+   *  @class p5.Reverb
+   *  @constructor
+   *  @example
+   *  <div><code>
+   *  var soundFile, reverb;
+   *  function preload() {
+   *    soundFile = loadSound('assets/Damscray_DancingTiger.mp3');
+   *  }
+   *
+   *  function setup() {
+   *    reverb = new p5.Reverb();
+   *    soundFile.disconnect(); // so we'll only hear reverb...
+   *
+   *    // connect soundFile to reverb, process w/
+   *    // 3 second reverbTime, decayRate of 2%
+   *    reverb.process(soundFile, 3, 2);
+   *    soundFile.play();
+   *  }
+   *  </code></div>
+   */
+  p5.Reverb = function () {
+    this.ac = p5sound.audiocontext;
+    this.convolverNode = this.ac.createConvolver();
+    this.input = this.ac.createGain();
+    this.output = this.ac.createGain();
+    // otherwise, Safari distorts
+    this.input.gain.value = 0.5;
+    this.input.connect(this.convolverNode);
+    this.convolverNode.connect(this.output);
+    // default params
+    this._seconds = 3;
+    this._decay = 2;
+    this._reverse = false;
+    this._buildImpulse();
+    this.connect();
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Connect a source to the reverb, and assign reverb parameters.
+   *  
+   *  @method  process
+   *  @param  {Object} src     p5.sound / Web Audio object with a sound
+   *                           output.
+   *  @param  {Number} [seconds] Duration of the reverb, in seconds.
+   *                           Min: 0, Max: 10. Defaults to 3.
+   *  @param  {Number} [decayRate] Percentage of decay with each echo.
+   *                            Min: 0, Max: 100. Defaults to 2.
+   *  @param  {Boolean} [reverse] Play the reverb backwards or forwards.
+   */
+  p5.Reverb.prototype.process = function (src, seconds, decayRate, reverse) {
+    src.connect(this.input);
+    var rebuild = false;
+    if (seconds) {
+      this._seconds = seconds;
+      rebuild = true;
+    }
+    if (decayRate) {
+      this._decay = decayRate;
+    }
+    if (reverse) {
+      this._reverse = reverse;
+    }
+    if (rebuild) {
+      this._buildImpulse();
+    }
+  };
+  /**
+   *  Set the reverb settings. Similar to .process(), but without
+   *  assigning a new input.
+   *  
+   *  @method  set
+   *  @param  {Number} [seconds] Duration of the reverb, in seconds.
+   *                           Min: 0, Max: 10. Defaults to 3.
+   *  @param  {Number} [decayRate] Percentage of decay with each echo.
+   *                            Min: 0, Max: 100. Defaults to 2.
+   *  @param  {Boolean} [reverse] Play the reverb backwards or forwards.
+   */
+  p5.Reverb.prototype.set = function (seconds, decayRate, reverse) {
+    var rebuild = false;
+    if (seconds) {
+      this._seconds = seconds;
+      rebuild = true;
+    }
+    if (decayRate) {
+      this._decay = decayRate;
+    }
+    if (reverse) {
+      this._reverse = reverse;
+    }
+    if (rebuild) {
+      this._buildImpulse();
+    }
+  };
+  /**
+   *  Set the output level of the delay effect.
+   *  
+   *  @method  amp
+   *  @param  {Number} volume amplitude between 0 and 1.0
+   *  @param  {Number} [rampTime] create a fade that lasts rampTime 
+   *  @param  {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   */
+  p5.Reverb.prototype.amp = function (vol, rampTime, tFromNow) {
+    var rampTime = rampTime || 0;
+    var tFromNow = tFromNow || 0;
+    var now = p5sound.audiocontext.currentTime;
+    var currentVol = this.output.gain.value;
+    this.output.gain.cancelScheduledValues(now);
+    this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
+    this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
+  };
+  /**
+   *  Send output to a p5.sound or web audio object
+   *  
+   *  @method  connect
+   *  @param  {Object} unit
+   */
+  p5.Reverb.prototype.connect = function (unit) {
+    var u = unit || p5.soundOut.input;
+    this.output.connect(u.input ? u.input : u);
+  };
+  /**
+   *  Disconnect all output.
+   *  
+   *  @method disconnect
+   */
+  p5.Reverb.prototype.disconnect = function () {
+    this.output.disconnect();
+  };
+  /**
+   *  Inspired by Simple Reverb by Jordan Santell
+   *  https://github.com/web-audio-components/simple-reverb/blob/master/index.js
+   * 
+   *  Utility function for building an impulse response
+   *  based on the module parameters.
+   *
+   *  @private
+   */
+  p5.Reverb.prototype._buildImpulse = function () {
+    var rate = this.ac.sampleRate;
+    var length = rate * this._seconds;
+    var decay = this._decay;
+    var impulse = this.ac.createBuffer(2, length, rate);
+    var impulseL = impulse.getChannelData(0);
+    var impulseR = impulse.getChannelData(1);
+    var n, i;
+    for (i = 0; i < length; i++) {
+      n = this.reverse ? length - i : i;
+      impulseL[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
+      impulseR[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
+    }
+    this.convolverNode.buffer = impulse;
+  };
+  p5.Reverb.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    if (this.convolverNode) {
+      this.convolverNode.buffer = null;
+      this.convolverNode = null;
+    }
+    if (typeof this.output !== 'undefined') {
+      this.output.disconnect();
+      this.output = null;
+    }
+    if (typeof this.panner !== 'undefined') {
+      this.panner.disconnect();
+      this.panner = null;
+    }
+  };
+  // =======================================================================
+  //                          *** p5.Convolver ***
+  // =======================================================================
+  /**
+   *  <p>p5.Convolver extends p5.Reverb. It can emulate the sound of real
+   *  physical spaces through a process called <a href="
+   *  https://en.wikipedia.org/wiki/Convolution_reverb#Real_space_simulation">
+   *  convolution</a>.</p>
+   *  
+   *  <p>Convolution multiplies any audio input by an "impulse response"
+   *  to simulate the dispersion of sound over time. The impulse response is
+   *  generated from an audio file that you provide. One way to
+   *  generate an impulse response is to pop a balloon in a reverberant space
+   *  and record the echo. Convolution can also be used to experiment with
+   *  sound.</p>
+   *
+   *  <p>Use the method <code>createConvolution(path)</code> to instantiate a
+   *  p5.Convolver with a path to your impulse response audio file.</p>
+   *  
+   *  @class p5.Convolver
+   *  @constructor
+   *  @param  {String}   path     path to a sound file
+   *  @param  {Function} [callback] function to call when loading succeeds
+   *  @param  {Function} [errorCallback] function to call if loading fails.
+   *                                     This function will receive an error or
+   *                                     XMLHttpRequest object with information
+   *                                     about what went wrong.
+   *  @example
+   *  <div><code>
+   *  var cVerb, sound;
+   *  function preload() {
+   *    // We have both MP3 and OGG versions of all sound assets
+   *    soundFormats('ogg', 'mp3');
+   *    
+   *    // Try replacing 'bx-spring' with other soundfiles like
+   *    // 'concrete-tunnel' 'small-plate' 'drum' 'beatbox'
+   *    cVerb = createConvolver('assets/bx-spring.mp3');
+   *
+   *    // Try replacing 'Damscray_DancingTiger' with
+   *    // 'beat', 'doorbell', lucky_dragons_-_power_melody'
+   *    sound = loadSound('assets/Damscray_DancingTiger.mp3');
+   *  }
+   *  
+   *  function setup() {
+   *    // disconnect from master output...
+   *    sound.disconnect();
+   *    
+   *    // ...and process with cVerb
+   *    // so that we only hear the convolution
+   *    cVerb.process(sound);
+   *    
+   *    sound.play();
+   *  }
+   *  </code></div>
+   */
+  p5.Convolver = function (path, callback, errorCallback) {
+    this.ac = p5sound.audiocontext;
+    /**
+     *  Internally, the p5.Convolver uses the a
+     *  <a href="http://www.w3.org/TR/webaudio/#ConvolverNode">
+     *  Web Audio Convolver Node</a>.
+     *  
+     *  @property convolverNode
+     *  @type {Object}  Web Audio Convolver Node
+     */
+    this.convolverNode = this.ac.createConvolver();
+    this.input = this.ac.createGain();
+    this.output = this.ac.createGain();
+    // otherwise, Safari distorts
+    this.input.gain.value = 0.5;
+    this.input.connect(this.convolverNode);
+    this.convolverNode.connect(this.output);
+    if (path) {
+      this.impulses = [];
+      this._loadBuffer(path, callback, errorCallback);
+    } else {
+      // parameters
+      this._seconds = 3;
+      this._decay = 2;
+      this._reverse = false;
+      this._buildImpulse();
+    }
+    this.connect();
+    p5sound.soundArray.push(this);
+  };
+  p5.Convolver.prototype = Object.create(p5.Reverb.prototype);
+  p5.prototype.registerPreloadMethod('createConvolver', p5.prototype);
+  /**
+   *  Create a p5.Convolver. Accepts a path to a soundfile 
+   *  that will be used to generate an impulse response.
+   *
+   *  @method  createConvolver
+   *  @param  {String}   path     path to a sound file
+   *  @param  {Function} [callback] function to call if loading is successful.
+   *                                The object will be passed in as the argument
+   *                                to the callback function.
+   *  @param  {Function} [errorCallback] function to call if loading is not successful.
+   *                                A custom error will be passed in as the argument
+   *                                to the callback function.
+   *  @return {p5.Convolver}
+   *  @example
+   *  <div><code>
+   *  var cVerb, sound;
+   *  function preload() {
+   *    // We have both MP3 and OGG versions of all sound assets
+   *    soundFormats('ogg', 'mp3');
+   *    
+   *    // Try replacing 'bx-spring' with other soundfiles like
+   *    // 'concrete-tunnel' 'small-plate' 'drum' 'beatbox'
+   *    cVerb = createConvolver('assets/bx-spring.mp3');
+   *
+   *    // Try replacing 'Damscray_DancingTiger' with
+   *    // 'beat', 'doorbell', lucky_dragons_-_power_melody'
+   *    sound = loadSound('assets/Damscray_DancingTiger.mp3');
+   *  }
+   *  
+   *  function setup() {
+   *    // disconnect from master output...
+   *    sound.disconnect();
+   *    
+   *    // ...and process with cVerb
+   *    // so that we only hear the convolution
+   *    cVerb.process(sound);
+   *    
+   *    sound.play();
+   *  }
+   *  </code></div>
+   */
+  p5.prototype.createConvolver = function (path, callback, errorCallback) {
+    // if loading locally without a server
+    if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
+      alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
+    }
+    var cReverb = new p5.Convolver(path, callback, errorCallback);
+    cReverb.impulses = [];
+    return cReverb;
+  };
+  /**
+   *  Private method to load a buffer as an Impulse Response,
+   *  assign it to the convolverNode, and add to the Array of .impulses.
+   *  
+   *  @param   {String}   path
+   *  @param   {Function} callback
+   *  @param   {Function} errorCallback
+   *  @private
+   */
+  p5.Convolver.prototype._loadBuffer = function (path, callback, errorCallback) {
+    var path = p5.prototype._checkFileFormats(path);
+    var self = this;
+    var errorTrace = new Error().stack;
+    var ac = p5.prototype.getAudioContext();
+    var request = new XMLHttpRequest();
+    request.open('GET', path, true);
+    request.responseType = 'arraybuffer';
+    request.onload = function () {
+      if (request.status == 200) {
+        // on success loading file:
+        ac.decodeAudioData(request.response, function (buff) {
+          var buffer = {};
+          var chunks = path.split('/');
+          buffer.name = chunks[chunks.length - 1];
+          buffer.audioBuffer = buff;
+          self.impulses.push(buffer);
+          self.convolverNode.buffer = buffer.audioBuffer;
+          if (callback) {
+            callback(buffer);
+          }
+        }, // error decoding buffer. "e" is undefined in Chrome 11/22/2015
+        function (e) {
+          var err = new CustomError('decodeAudioData', errorTrace, self.url);
+          var msg = 'AudioContext error at decodeAudioData for ' + self.url;
+          if (errorCallback) {
+            err.msg = msg;
+            errorCallback(err);
+          } else {
+            console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+          }
+        });
+      } else {
+        var err = new CustomError('loadConvolver', errorTrace, self.url);
+        var msg = 'Unable to load ' + self.url + '. The request status was: ' + request.status + ' (' + request.statusText + ')';
+        if (errorCallback) {
+          err.message = msg;
+          errorCallback(err);
+        } else {
+          console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+        }
+      }
+    };
+    // if there is another error, aside from 404...
+    request.onerror = function (e) {
+      var err = new CustomError('loadConvolver', errorTrace, self.url);
+      var msg = 'There was no response from the server at ' + self.url + '. Check the url and internet connectivity.';
+      if (errorCallback) {
+        err.message = msg;
+        errorCallback(err);
+      } else {
+        console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+      }
+    };
+    request.send();
+  };
+  p5.Convolver.prototype.set = null;
+  /**
+   *  Connect a source to the reverb, and assign reverb parameters.
+   *  
+   *  @method  process
+   *  @param  {Object} src     p5.sound / Web Audio object with a sound
+   *                           output.
+   *  @example
+   *  <div><code>
+   *  var cVerb, sound;
+   *  function preload() {
+   *    soundFormats('ogg', 'mp3');
+   *    
+   *    cVerb = createConvolver('assets/concrete-tunnel.mp3');
+   *
+   *    sound = loadSound('assets/beat.mp3');
+   *  }
+   *  
+   *  function setup() {
+   *    // disconnect from master output...
+   *    sound.disconnect();
+   *    
+   *    // ...and process with (i.e. connect to) cVerb
+   *    // so that we only hear the convolution
+   *    cVerb.process(sound);
+   *    
+   *    sound.play();
+   *  }
+   *  </code></div>
+   */
+  p5.Convolver.prototype.process = function (src) {
+    src.connect(this.input);
+  };
+  /**
+   *  If you load multiple impulse files using the .addImpulse method,
+   *  they will be stored as Objects in this Array. Toggle between them
+   *  with the <code>toggleImpulse(id)</code> method.
+   *  
+   *  @property impulses
+   *  @type {Array} Array of Web Audio Buffers
+   */
+  p5.Convolver.prototype.impulses = [];
+  /**
+   *  Load and assign a new Impulse Response to the p5.Convolver.
+   *  The impulse is added to the <code>.impulses</code> array. Previous
+   *  impulses can be accessed with the <code>.toggleImpulse(id)</code>
+   *  method.
+   *  
+   *  @method  addImpulse
+   *  @param  {String}   path     path to a sound file
+   *  @param  {Function} callback function (optional)
+   *  @param  {Function} errorCallback function (optional)
+   */
+  p5.Convolver.prototype.addImpulse = function (path, callback, errorCallback) {
+    // if loading locally without a server
+    if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
+      alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
+    }
+    this._loadBuffer(path, callback, errorCallback);
+  };
+  /**
+   *  Similar to .addImpulse, except that the <code>.impulses</code>
+   *  Array is reset to save memory. A new <code>.impulses</code>
+   *  array is created with this impulse as the only item. 
+   *
+   *  @method  resetImpulse
+   *  @param  {String}   path     path to a sound file
+   *  @param  {Function} callback function (optional)
+   *  @param  {Function} errorCallback function (optional)
+   */
+  p5.Convolver.prototype.resetImpulse = function (path, callback, errorCallback) {
+    // if loading locally without a server
+    if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
+      alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
+    }
+    this.impulses = [];
+    this._loadBuffer(path, callback, errorCallback);
+  };
+  /**
+   *  If you have used <code>.addImpulse()</code> to add multiple impulses
+   *  to a p5.Convolver, then you can use this method to toggle between
+   *  the items in the <code>.impulses</code> Array. Accepts a parameter
+   *  to identify which impulse you wish to use, identified either by its
+   *  original filename (String) or by its position in the <code>.impulses
+   *  </code> Array (Number).<br/>
+   *  You can access the objects in the .impulses Array directly. Each
+   *  Object has two attributes: an <code>.audioBuffer</code> (type:
+   *  Web Audio <a href="
+   *  http://webaudio.github.io/web-audio-api/#the-audiobuffer-interface">
+   *  AudioBuffer)</a> and a <code>.name</code>, a String that corresponds
+   *  with the original filename. 
+   *  
+   *  @method toggleImpulse
+   *  @param {String|Number} id Identify the impulse by its original filename
+   *                            (String), or by its position in the
+   *                            <code>.impulses</code> Array (Number).
+   */
+  p5.Convolver.prototype.toggleImpulse = function (id) {
+    if (typeof id === 'number' && id < this.impulses.length) {
+      this.convolverNode.buffer = this.impulses[id].audioBuffer;
+    }
+    if (typeof id === 'string') {
+      for (var i = 0; i < this.impulses.length; i++) {
+        if (this.impulses[i].name === id) {
+          this.convolverNode.buffer = this.impulses[i].audioBuffer;
+          break;
+        }
+      }
+    }
+  };
+  p5.Convolver.prototype.dispose = function () {
+    // remove all the Impulse Response buffers
+    for (var i in this.impulses) {
+      this.impulses[i] = null;
+    }
+    this.convolverNode.disconnect();
+    this.concolverNode = null;
+    if (typeof this.output !== 'undefined') {
+      this.output.disconnect();
+      this.output = null;
+    }
+    if (typeof this.panner !== 'undefined') {
+      this.panner.disconnect();
+      this.panner = null;
+    }
+  };
+}(master, errorHandler, sndcore);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_core_TimelineState;
+Tone_core_TimelineState = function (Tone) {
+  'use strict';
+  Tone.TimelineState = function (initial) {
+    Tone.Timeline.call(this);
+    this._initial = initial;
+  };
+  Tone.extend(Tone.TimelineState, Tone.Timeline);
+  Tone.TimelineState.prototype.getStateAtTime = function (time) {
+    var event = this.getEvent(time);
+    if (event !== null) {
+      return event.state;
+    } else {
+      return this._initial;
+    }
+  };
+  Tone.TimelineState.prototype.setStateAtTime = function (state, time) {
+    this.addEvent({
+      'state': state,
+      'time': this.toSeconds(time)
+    });
+  };
+  return Tone.TimelineState;
+}(Tone_core_Tone, Tone_core_Timeline);
+/** Tone.js module by Yotam Mann, MIT License 2016  http://opensource.org/licenses/MIT **/
+var Tone_core_Clock;
+Tone_core_Clock = function (Tone) {
+  'use strict';
+  Tone.Clock = function () {
+    var options = this.optionsObject(arguments, [
+      'callback',
+      'frequency'
+    ], Tone.Clock.defaults);
+    this.callback = options.callback;
+    this._lookAhead = 'auto';
+    this._computedLookAhead = 1 / 60;
+    this._threshold = 0.5;
+    this._nextTick = -1;
+    this._lastUpdate = 0;
+    this._loopID = -1;
+    this.frequency = new Tone.TimelineSignal(options.frequency, Tone.Type.Frequency);
+    this.ticks = 0;
+    this._state = new Tone.TimelineState(Tone.State.Stopped);
+    this._boundLoop = this._loop.bind(this);
+    this._readOnly('frequency');
+    this._loop();
+  };
+  Tone.extend(Tone.Clock);
+  Tone.Clock.defaults = {
+    'callback': Tone.noOp,
+    'frequency': 1,
+    'lookAhead': 'auto'
+  };
+  Object.defineProperty(Tone.Clock.prototype, 'state', {
+    get: function () {
+      return this._state.getStateAtTime(this.now());
+    }
+  });
+  Object.defineProperty(Tone.Clock.prototype, 'lookAhead', {
+    get: function () {
+      return this._lookAhead;
+    },
+    set: function (val) {
+      if (val === 'auto') {
+        this._lookAhead = 'auto';
+      } else {
+        this._lookAhead = this.toSeconds(val);
+      }
+    }
+  });
+  Tone.Clock.prototype.start = function (time, offset) {
+    time = this.toSeconds(time);
+    if (this._state.getStateAtTime(time) !== Tone.State.Started) {
+      this._state.addEvent({
+        'state': Tone.State.Started,
+        'time': time,
+        'offset': offset
+      });
+    }
+    return this;
+  };
+  Tone.Clock.prototype.stop = function (time) {
+    time = this.toSeconds(time);
+    if (this._state.getStateAtTime(time) !== Tone.State.Stopped) {
+      this._state.setStateAtTime(Tone.State.Stopped, time);
+    }
+    return this;
+  };
+  Tone.Clock.prototype.pause = function (time) {
+    time = this.toSeconds(time);
+    if (this._state.getStateAtTime(time) === Tone.State.Started) {
+      this._state.setStateAtTime(Tone.State.Paused, time);
+    }
+    return this;
+  };
+  Tone.Clock.prototype._loop = function (time) {
+    this._loopID = requestAnimationFrame(this._boundLoop);
+    if (this._lookAhead === 'auto') {
+      if (!this.isUndef(time)) {
+        var diff = (time - this._lastUpdate) / 1000;
+        this._lastUpdate = time;
+        if (diff < this._threshold) {
+          this._computedLookAhead = (9 * this._computedLookAhead + diff) / 10;
+        }
+      }
+    } else {
+      this._computedLookAhead = this._lookAhead;
+    }
+    var now = this.now();
+    var lookAhead = this._computedLookAhead * 2;
+    var event = this._state.getEvent(now + lookAhead);
+    var state = Tone.State.Stopped;
+    if (event) {
+      state = event.state;
+      if (this._nextTick === -1 && state === Tone.State.Started) {
+        this._nextTick = event.time;
+        if (!this.isUndef(event.offset)) {
+          this.ticks = event.offset;
+        }
+      }
+    }
+    if (state === Tone.State.Started) {
+      while (now + lookAhead > this._nextTick) {
+        if (now > this._nextTick + this._threshold) {
+          this._nextTick = now;
+        }
+        var tickTime = this._nextTick;
+        this._nextTick += 1 / this.frequency.getValueAtTime(this._nextTick);
+        this.callback(tickTime);
+        this.ticks++;
+      }
+    } else if (state === Tone.State.Stopped) {
+      this._nextTick = -1;
+      this.ticks = 0;
+    }
+  };
+  Tone.Clock.prototype.getStateAtTime = function (time) {
+    return this._state.getStateAtTime(time);
+  };
+  Tone.Clock.prototype.dispose = function () {
+    cancelAnimationFrame(this._loopID);
+    Tone.TimelineState.prototype.dispose.call(this);
+    this._writable('frequency');
+    this.frequency.dispose();
+    this.frequency = null;
+    this._boundLoop = Tone.noOp;
+    this._nextTick = Infinity;
+    this.callback = null;
+    this._state.dispose();
+    this._state = null;
+  };
+  return Tone.Clock;
+}(Tone_core_Tone, Tone_signal_TimelineSignal);
+var metro;
+metro = function () {
+  'use strict';
+  var p5sound = master;
+  // requires the Tone.js library's Clock (MIT license, Yotam Mann)
+  // https://github.com/TONEnoTONE/Tone.js/
+  var Clock = Tone_core_Clock;
+  var ac = p5sound.audiocontext;
+  // var upTick = false;
+  p5.Metro = function () {
+    this.clock = new Clock({ 'callback': this.ontick.bind(this) });
+    this.syncedParts = [];
+    this.bpm = 120;
+    // gets overridden by p5.Part
+    this._init();
+    this.tickCallback = function () {
+    };
+  };
+  var prevTick = 0;
+  var tatumTime = 0;
+  p5.Metro.prototype.ontick = function (tickTime) {
+    var elapsedTime = tickTime - prevTick;
+    var secondsFromNow = tickTime - p5sound.audiocontext.currentTime;
+    if (elapsedTime - tatumTime <= -0.02) {
+      return;
+    } else {
+      prevTick = tickTime;
+      // for all of the active things on the metro:
+      for (var i in this.syncedParts) {
+        var thisPart = this.syncedParts[i];
+        thisPart.incrementStep(secondsFromNow);
+        // each synced source keeps track of its own beat number
+        for (var j in thisPart.phrases) {
+          var thisPhrase = thisPart.phrases[j];
+          var phraseArray = thisPhrase.sequence;
+          var bNum = this.metroTicks % phraseArray.length;
+          if (phraseArray[bNum] !== 0 && (this.metroTicks < phraseArray.length || !thisPhrase.looping)) {
+            thisPhrase.callback(secondsFromNow, phraseArray[bNum]);
+          }
+        }
+      }
+      this.metroTicks += 1;
+      this.tickCallback(secondsFromNow);
+    }
+  };
+  p5.Metro.prototype.setBPM = function (bpm, rampTime) {
+    var beatTime = 60 / (bpm * this.tatums);
+    var now = p5sound.audiocontext.currentTime;
+    tatumTime = beatTime;
+    var rampTime = rampTime || 0;
+    this.clock.frequency.setValueAtTime(this.clock.frequency.value, now);
+    this.clock.frequency.linearRampToValueAtTime(bpm, now + rampTime);
+    this.bpm = bpm;
+  };
+  p5.Metro.prototype.getBPM = function (tempo) {
+    return this.clock.getRate() / this.tatums * 60;
+  };
+  p5.Metro.prototype._init = function () {
+    this.metroTicks = 0;
+  };
+  // clear existing synced parts, add only this one
+  p5.Metro.prototype.resetSync = function (part) {
+    this.syncedParts = [part];
+  };
+  // push a new synced part to the array
+  p5.Metro.prototype.pushSync = function (part) {
+    this.syncedParts.push(part);
+  };
+  p5.Metro.prototype.start = function (timeFromNow) {
+    var t = timeFromNow || 0;
+    var now = p5sound.audiocontext.currentTime;
+    this.clock.start(now + t);
+    this.setBPM(this.bpm);
+  };
+  p5.Metro.prototype.stop = function (timeFromNow) {
+    var t = timeFromNow || 0;
+    var now = p5sound.audiocontext.currentTime;
+    if (this.clock._oscillator) {
+      this.clock.stop(now + t);
+    }
+  };
+  p5.Metro.prototype.beatLength = function (tatums) {
+    this.tatums = 1 / tatums / 4;
+  };
+}(master, Tone_core_Clock);
+var looper;
+looper = function () {
+  'use strict';
+  var p5sound = master;
+  var bpm = 120;
+  /**
+   *  Set the global tempo, in beats per minute, for all
+   *  p5.Parts. This method will impact all active p5.Parts.
+   *  
+   *  @param {Number} BPM      Beats Per Minute
+   *  @param {Number} rampTime Seconds from now
+   */
+  p5.prototype.setBPM = function (BPM, rampTime) {
+    bpm = BPM;
+    for (var i in p5sound.parts) {
+      p5sound.parts[i].setBPM(bpm, rampTime);
+    }
+  };
+  /**
+   *  <p>A phrase is a pattern of musical events over time, i.e.
+   *  a series of notes and rests.</p>
+   *  
+   *  <p>Phrases must be added to a p5.Part for playback, and
+   *  each part can play multiple phrases at the same time.
+   *  For example, one Phrase might be a kick drum, another
+   *  could be a snare, and another could be the bassline.</p>
+   *  
+   *  <p>The first parameter is a name so that the phrase can be
+   *  modified or deleted later. The callback is a a function that
+   *  this phrase will call at every step—for example it might be
+   *  called <code>playNote(value){}</code>. The array determines
+   *  which value is passed into the callback at each step of the
+   *  phrase. It can be numbers, an object with multiple numbers,
+   *  or a zero (0) indicates a rest so the callback won't be called).</p>
+   * 
+   *  @class p5.Phrase
+   *  @constructor
+   *  @param {String}   name     Name so that you can access the Phrase.
+   *  @param {Function} callback The name of a function that this phrase
+   *                             will call. Typically it will play a sound,
+   *                             and accept two parameters: a time at which
+   *                             to play the sound (in seconds from now),
+   *                             and a value from the sequence array. The
+   *                             time should be passed into the play() or
+   *                             start() method to ensure precision.
+   *  @param {Array}   sequence    Array of values to pass into the callback
+   *                            at each step of the phrase.
+   *  @example
+   *  <div><code>
+   *  var mySound, myPhrase, myPart;
+   *  var pattern = [1,0,0,2,0,2,0,0];
+   *  var msg = 'click to play';
+   *  
+   *  function preload() {
+   *    mySound = loadSound('assets/beatbox.mp3');
+   *  }
+   *  
+   *  function setup() {
+   *    noStroke();
+   *    fill(255);
+   *    textAlign(CENTER);
+   *    masterVolume(0.1);
+   *    
+   *    myPhrase = new p5.Phrase('bbox', makeSound, pattern);
+   *    myPart = new p5.Part();
+   *    myPart.addPhrase(myPhrase);
+   *    myPart.setBPM(60);
+   *  }
+   *
+   *  function draw() {
+   *    background(0);
+   *    text(msg, width/2, height/2);
+   *  }
+   *
+   *  function makeSound(time, playbackRate) {
+   *    mySound.rate(playbackRate);
+   *    mySound.play(time);
+   *  }
+   *  
+   *  function mouseClicked() {
+   *    if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+   *      myPart.start();
+   *      msg = 'playing pattern';
+   *    }
+   *  }
+   *
+   *  </code></div>
+   */
+  p5.Phrase = function (name, callback, sequence) {
+    this.phraseStep = 0;
+    this.name = name;
+    this.callback = callback;
+    /**
+     * Array of values to pass into the callback
+     * at each step of the phrase. Depending on the callback
+     * function's requirements, these values may be numbers,
+     * strings, or an object with multiple parameters.
+     * Zero (0) indicates a rest.
+     * 
+     * @property sequence
+     * @type {Array}
+     */
+    this.sequence = sequence;
+  };
+  /**
+   *  <p>A p5.Part plays back one or more p5.Phrases. Instantiate a part
+   *  with steps and tatums. By default, each step represents 1/16th note.</p>
+   *
+   *  <p>See p5.Phrase for more about musical timing.</p>
+   *  
+   *  @class p5.Part
+   *  @constructor
+   *  @param {Number} [steps]   Steps in the part
+   *  @param {Number} [tatums] Divisions of a beat (default is 1/16, a quarter note)
+   *  @example
+   *  <div><code>
+   *  var box, drum, myPart;
+   *  var boxPat = [1,0,0,2,0,2,0,0];
+   *  var drumPat = [0,1,1,0,2,0,1,0];
+   *  var msg = 'click to play';
+   *  
+   *  function preload() {
+   *    box = loadSound('assets/beatbox.mp3');
+   *    drum = loadSound('assets/drum.mp3');
+   *  }
+   *  
+   *  function setup() {
+   *    noStroke();
+   *    fill(255);
+   *    textAlign(CENTER);
+   *    masterVolume(0.1);
+   *
+   *    var boxPhrase = new p5.Phrase('box', playBox, boxPat);
+   *    var drumPhrase = new p5.Phrase('drum', playDrum, drumPat);
+   *    myPart = new p5.Part();
+   *    myPart.addPhrase(boxPhrase);
+   *    myPart.addPhrase(drumPhrase);
+   *    myPart.setBPM(60);
+   *    masterVolume(0.1);
+   *  }
+   *
+   *  function draw() {
+   *    background(0);
+   *    text(msg, width/2, height/2);
+   *  }
+   *
+   *  function playBox(time, playbackRate) {
+   *    box.rate(playbackRate);
+   *    box.play(time);
+   *  }
+   *  
+   *  function playDrum(time, playbackRate) {
+   *    drum.rate(playbackRate);
+   *    drum.play(time);
+   *  }
+   *  
+   *  function mouseClicked() {
+   *    if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+   *      myPart.start();
+   *      msg = 'playing part';
+   *    }
+   *  }
+   *  </code></div>
+   */
+  p5.Part = function (steps, bLength) {
+    this.length = steps || 0;
+    // how many beats
+    this.partStep = 0;
+    this.phrases = [];
+    this.looping = false;
+    this.isPlaying = false;
+    // what does this looper do when it gets to the last step?
+    this.onended = function () {
+      this.stop();
+    };
+    this.tatums = bLength || 0.0625;
+    // defaults to quarter note
+    this.metro = new p5.Metro();
+    this.metro._init();
+    this.metro.beatLength(this.tatums);
+    this.metro.setBPM(bpm);
+    p5sound.parts.push(this);
+    this.callback = function () {
+    };
+  };
+  /**
+   *  Set the tempo of this part, in Beats Per Minute. 
+   *  
+   *  @method  setBPM
+   *  @param {Number} BPM      Beats Per Minute
+   *  @param {Number} [rampTime] Seconds from now
+   */
+  p5.Part.prototype.setBPM = function (tempo, rampTime) {
+    this.metro.setBPM(tempo, rampTime);
+  };
+  /**
+   *  Returns the Beats Per Minute of this currently part.
+   *  
+   *  @method getBPM
+   *  @return {Number} 
+   */
+  p5.Part.prototype.getBPM = function () {
+    return this.metro.getBPM();
+  };
+  /**
+   *  Start playback of this part. It will play
+   *  through all of its phrases at a speed
+   *  determined by setBPM.
+   *  
+   *  @method  start
+   *  @param  {Number} [time] seconds from now
+   */
+  p5.Part.prototype.start = function (time) {
+    if (!this.isPlaying) {
+      this.isPlaying = true;
+      this.metro.resetSync(this);
+      var t = time || 0;
+      this.metro.start(t);
+    }
+  };
+  /**
+   *  Loop playback of this part. It will begin
+   *  looping through all of its phrases at a speed
+   *  determined by setBPM.
+   *  
+   *  @method  loop
+   *  @param  {Number} [time] seconds from now
+   */
+  p5.Part.prototype.loop = function (time) {
+    this.looping = true;
+    // rest onended function
+    this.onended = function () {
+      this.partStep = 0;
+    };
+    var t = time || 0;
+    this.start(t);
+  };
+  /**
+   *  Tell the part to stop looping.
+   *
+   *  @method  noLoop
+   */
+  p5.Part.prototype.noLoop = function () {
+    this.looping = false;
+    // rest onended function
+    this.onended = function () {
+      this.stop();
+    };
+  };
+  /**
+   *  Stop the part and cue it to step 0.
+   *  
+   *  @method  stop
+   *  @param  {Number} [time] seconds from now
+   */
+  p5.Part.prototype.stop = function (time) {
+    this.partStep = 0;
+    this.pause(time);
+  };
+  /**
+   *  Pause the part. Playback will resume
+   *  from the current step.
+   *  
+   *  @method  pause
+   *  @param  {Number} time seconds from now
+   */
+  p5.Part.prototype.pause = function (time) {
+    this.isPlaying = false;
+    var t = time || 0;
+    this.metro.stop(t);
+  };
+  /**
+   *  Add a p5.Phrase to this Part.
+   *
+   *  @method  addPhrase
+   *  @param {p5.Phrase}   phrase   reference to a p5.Phrase
+   */
+  p5.Part.prototype.addPhrase = function (name, callback, array) {
+    var p;
+    if (arguments.length === 3) {
+      p = new p5.Phrase(name, callback, array);
+    } else if (arguments[0] instanceof p5.Phrase) {
+      p = arguments[0];
+    } else {
+      throw 'invalid input. addPhrase accepts name, callback, array or a p5.Phrase';
+    }
+    this.phrases.push(p);
+    // reset the length if phrase is longer than part's existing length
+    if (p.sequence.length > this.length) {
+      this.length = p.sequence.length;
+    }
+  };
+  /**
+   *  Remove a phrase from this part, based on the name it was
+   *  given when it was created.
+   *  
+   *  @method  removePhrase
+   *  @param  {String} phraseName
+   */
+  p5.Part.prototype.removePhrase = function (name) {
+    for (var i in this.phrases) {
+      if (this.phrases[i].name === name) {
+        this.phrases.split(i, 1);
+      }
+    }
+  };
+  /**
+   *  Get a phrase from this part, based on the name it was
+   *  given when it was created. Now you can modify its array.
+   *  
+   *  @method  getPhrase
+   *  @param  {String} phraseName
+   */
+  p5.Part.prototype.getPhrase = function (name) {
+    for (var i in this.phrases) {
+      if (this.phrases[i].name === name) {
+        return this.phrases[i];
+      }
+    }
+  };
+  /**
+   *  Get a phrase from this part, based on the name it was
+   *  given when it was created. Now you can modify its array.
+   *  
+   *  @method  replaceSequence
+   *  @param  {String} phraseName
+   *  @param  {Array} sequence  Array of values to pass into the callback
+   *                            at each step of the phrase.
+   */
+  p5.Part.prototype.replaceSequence = function (name, array) {
+    for (var i in this.phrases) {
+      if (this.phrases[i].name === name) {
+        this.phrases[i].sequence = array;
+      }
+    }
+  };
+  p5.Part.prototype.incrementStep = function (time) {
+    if (this.partStep < this.length - 1) {
+      this.callback(time);
+      this.partStep += 1;
+    } else {
+      if (this.looping) {
+        this.callback(time);
+      }
+      this.onended();
+      this.partStep = 0;
+    }
+  };
+  /**
+   *  Fire a callback function at every step.
+   *
+   *  @method onStep
+   *  @param  {Function} callback The name of the callback
+   *                              you want to fire
+   *                              on every beat/tatum.
+   */
+  p5.Part.prototype.onStep = function (callback) {
+    this.callback = callback;
+  };
+  // ===============
+  // p5.Score
+  // ===============
+  /**
+   *  A Score consists of a series of Parts. The parts will
+   *  be played back in order. For example, you could have an
+   *  A part, a B part, and a C part, and play them back in this order
+   *  <code>new p5.Score(a, a, b, a, c)</code>
+   *
+   *  @class p5.Score
+   *  @constructor
+   *  @param {p5.Part} part(s) One or multiple parts, to be played in sequence.
+   *  @return {p5.Score}
+   */
+  p5.Score = function () {
+    // for all of the arguments
+    this.parts = [];
+    this.currentPart = 0;
+    var thisScore = this;
+    for (var i in arguments) {
+      this.parts[i] = arguments[i];
+      this.parts[i].nextPart = this.parts[i + 1];
+      this.parts[i].onended = function () {
+        thisScore.resetPart(i);
+        playNextPart(thisScore);
+      };
+    }
+    this.looping = false;
+  };
+  p5.Score.prototype.onended = function () {
+    if (this.looping) {
+      // this.resetParts();
+      this.parts[0].start();
+    } else {
+      this.parts[this.parts.length - 1].onended = function () {
+        this.stop();
+        this.resetParts();
+      };
+    }
+    this.currentPart = 0;
+  };
+  /**
+   *  Start playback of the score.
+   *  
+   *  @method  start
+   */
+  p5.Score.prototype.start = function () {
+    this.parts[this.currentPart].start();
+    this.scoreStep = 0;
+  };
+  /**
+   *  Stop playback of the score.
+   *  
+   *  @method  stop
+   */
+  p5.Score.prototype.stop = function () {
+    this.parts[this.currentPart].stop();
+    this.currentPart = 0;
+    this.scoreStep = 0;
+  };
+  /**
+   *  Pause playback of the score.
+   *  
+   *  @method  pause
+   */
+  p5.Score.prototype.pause = function () {
+    this.parts[this.currentPart].stop();
+  };
+  /**
+   *  Loop playback of the score.
+   *  
+   *  @method  loop
+   */
+  p5.Score.prototype.loop = function () {
+    this.looping = true;
+    this.start();
+  };
+  /**
+   *  Stop looping playback of the score. If it
+   *  is currently playing, this will go into effect
+   *  after the current round of playback completes.
+   *  
+   *  @method  noLoop
+   */
+  p5.Score.prototype.noLoop = function () {
+    this.looping = false;
+  };
+  p5.Score.prototype.resetParts = function () {
+    for (var i in this.parts) {
+      this.resetPart(i);
+    }
+  };
+  p5.Score.prototype.resetPart = function (i) {
+    this.parts[i].stop();
+    this.parts[i].partStep = 0;
+    for (var p in this.parts[i].phrases) {
+      this.parts[i].phrases[p].phraseStep = 0;
+    }
+  };
+  /**
+   *  Set the tempo for all parts in the score
+   *  
+   *  @param {Number} BPM      Beats Per Minute
+   *  @param {Number} rampTime Seconds from now
+   */
+  p5.Score.prototype.setBPM = function (bpm, rampTime) {
+    for (var i in this.parts) {
+      this.parts[i].setBPM(bpm, rampTime);
+    }
+  };
+  function playNextPart(aScore) {
+    aScore.currentPart++;
+    if (aScore.currentPart >= aScore.parts.length) {
+      aScore.scoreStep = 0;
+      aScore.onended();
+    } else {
+      aScore.scoreStep = 0;
+      aScore.parts[aScore.currentPart - 1].stop();
+      aScore.parts[aScore.currentPart].start();
+    }
+  }
+}(master);
+var soundRecorder;
+soundRecorder = function () {
+  'use strict';
+  var p5sound = master;
+  var ac = p5sound.audiocontext;
+  /**
+   *  <p>Record sounds for playback and/or to save as a .wav file.
+   *  The p5.SoundRecorder records all sound output from your sketch,
+   *  or can be assigned a specific source with setInput().</p>
+   *  <p>The record() method accepts a p5.SoundFile as a parameter.
+   *  When playback is stopped (either after the given amount of time,
+   *  or with the stop() method), the p5.SoundRecorder will send its
+   *  recording to that p5.SoundFile for playback.</p>
+   *  
+   *  @class p5.SoundRecorder
+   *  @constructor
+   *  @example
+   *  <div><code>
+   *  var mic, recorder, soundFile;
+   *  var state = 0;
+   *  
+   *  function setup() {
+   *    background(200);
+   *    // create an audio in
+   *    mic = new p5.AudioIn();
+   *    
+   *    // prompts user to enable their browser mic
+   *    mic.start();
+   *    
+   *    // create a sound recorder
+   *    recorder = new p5.SoundRecorder();
+   *    
+   *    // connect the mic to the recorder
+   *    recorder.setInput(mic);
+   *    
+   *    // this sound file will be used to
+   *    // playback & save the recording
+   *    soundFile = new p5.SoundFile();
+   *
+   *    text('keyPress to record', 20, 20);
+   *  }
+   *
+   *  function keyPressed() {
+   *    // make sure user enabled the mic
+   *    if (state === 0 && mic.enabled) {
+   *    
+   *      // record to our p5.SoundFile
+   *      recorder.record(soundFile);
+   *      
+   *      background(255,0,0);
+   *      text('Recording!', 20, 20);
+   *      state++;
+   *    }
+   *    else if (state === 1) {
+   *      background(0,255,0);
+   *
+   *      // stop recorder and
+   *      // send result to soundFile
+   *      recorder.stop(); 
+   *      
+   *      text('Stopped', 20, 20);
+   *      state++;
+   *    }
+   *    
+   *    else if (state === 2) {
+   *      soundFile.play(); // play the result!
+   *      save(soundFile, 'mySound.wav');
+   *      state++;
+   *    }
+   *  }
+   *  </div></code>
+   */
+  p5.SoundRecorder = function () {
+    this.input = ac.createGain();
+    this.output = ac.createGain();
+    this.recording = false;
+    this.bufferSize = 1024;
+    this._channels = 2;
+    // stereo (default)
+    this._clear();
+    // initialize variables
+    this._jsNode = ac.createScriptProcessor(this.bufferSize, this._channels, 2);
+    this._jsNode.onaudioprocess = this._audioprocess.bind(this);
+    /** 
+     *  callback invoked when the recording is over
+     *  @private
+     *  @type {function(Float32Array)}
+     */
+    this._callback = function () {
+    };
+    // connections
+    this._jsNode.connect(p5.soundOut._silentNode);
+    this.setInput();
+    // add this p5.SoundFile to the soundArray
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Connect a specific device to the p5.SoundRecorder.
+   *  If no parameter is given, p5.SoundRecorer will record
+   *  all audible p5.sound from your sketch.
+   *  
+   *  @method  setInput
+   *  @param {Object} [unit] p5.sound object or a web audio unit
+   *                         that outputs sound
+   */
+  p5.SoundRecorder.prototype.setInput = function (unit) {
+    this.input.disconnect();
+    this.input = null;
+    this.input = ac.createGain();
+    this.input.connect(this._jsNode);
+    this.input.connect(this.output);
+    if (unit) {
+      unit.connect(this.input);
+    } else {
+      p5.soundOut.output.connect(this.input);
+    }
+  };
+  /**
+   *  Start recording. To access the recording, provide
+   *  a p5.SoundFile as the first parameter. The p5.SoundRecorder
+   *  will send its recording to that p5.SoundFile for playback once
+   *  recording is complete. Optional parameters include duration
+   *  (in seconds) of the recording, and a callback function that
+   *  will be called once the complete recording has been
+   *  transfered to the p5.SoundFile.
+   *  
+   *  @method  record
+   *  @param  {p5.SoundFile}   soundFile    p5.SoundFile
+   *  @param  {Number}   [duration] Time (in seconds)
+   *  @param  {Function} [callback] The name of a function that will be
+   *                                called once the recording completes
+   */
+  p5.SoundRecorder.prototype.record = function (sFile, duration, callback) {
+    this.recording = true;
+    if (duration) {
+      this.sampleLimit = Math.round(duration * ac.sampleRate);
+    }
+    if (sFile && callback) {
+      this._callback = function () {
+        this.buffer = this._getBuffer();
+        sFile.setBuffer(this.buffer);
+        callback();
+      };
+    } else if (sFile) {
+      this._callback = function () {
+        this.buffer = this._getBuffer();
+        sFile.setBuffer(this.buffer);
+      };
+    }
+  };
+  /**
+   *  Stop the recording. Once the recording is stopped,
+   *  the results will be sent to the p5.SoundFile that
+   *  was given on .record(), and if a callback function
+   *  was provided on record, that function will be called.
+   *  
+   *  @method  stop
+   */
+  p5.SoundRecorder.prototype.stop = function () {
+    this.recording = false;
+    this._callback();
+    this._clear();
+  };
+  p5.SoundRecorder.prototype._clear = function () {
+    this._leftBuffers = [];
+    this._rightBuffers = [];
+    this.recordedSamples = 0;
+    this.sampleLimit = null;
+  };
+  /**
+   *  internal method called on audio process
+   *  
+   *  @private
+   *  @param   {AudioProcessorEvent} event 
+   */
+  p5.SoundRecorder.prototype._audioprocess = function (event) {
+    if (this.recording === false) {
+      return;
+    } else if (this.recording === true) {
+      // if we are past the duration, then stop... else:
+      if (this.sampleLimit && this.recordedSamples >= this.sampleLimit) {
+        this.stop();
+      } else {
+        // get channel data
+        var left = event.inputBuffer.getChannelData(0);
+        var right = event.inputBuffer.getChannelData(1);
+        // clone the samples
+        this._leftBuffers.push(new Float32Array(left));
+        this._rightBuffers.push(new Float32Array(right));
+        this.recordedSamples += this.bufferSize;
+      }
+    }
+  };
+  p5.SoundRecorder.prototype._getBuffer = function () {
+    var buffers = [];
+    buffers.push(this._mergeBuffers(this._leftBuffers));
+    buffers.push(this._mergeBuffers(this._rightBuffers));
+    return buffers;
+  };
+  p5.SoundRecorder.prototype._mergeBuffers = function (channelBuffer) {
+    var result = new Float32Array(this.recordedSamples);
+    var offset = 0;
+    var lng = channelBuffer.length;
+    for (var i = 0; i < lng; i++) {
+      var buffer = channelBuffer[i];
+      result.set(buffer, offset);
+      offset += buffer.length;
+    }
+    return result;
+  };
+  p5.SoundRecorder.prototype.dispose = function () {
+    this._clear();
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this._callback = function () {
+    };
+    if (this.input) {
+      this.input.disconnect();
+    }
+    this.input = null;
+    this._jsNode = null;
+  };
+  /**
+   *  Save a p5.SoundFile as a .wav audio file.
+   *  
+   *  @method saveSound
+   *  @param  {p5.SoundFile} soundFile p5.SoundFile that you wish to save
+   *  @param  {String} name      name of the resulting .wav file.
+   */
+  p5.prototype.saveSound = function (soundFile, name) {
+    var leftChannel = soundFile.buffer.getChannelData(0);
+    var rightChannel = soundFile.buffer.getChannelData(1);
+    var interleaved = interleave(leftChannel, rightChannel);
+    // create the buffer and view to create the .WAV file
+    var buffer = new ArrayBuffer(44 + interleaved.length * 2);
+    var view = new DataView(buffer);
+    // write the WAV container,
+    // check spec at: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+    // RIFF chunk descriptor
+    writeUTFBytes(view, 0, 'RIFF');
+    view.setUint32(4, 44 + interleaved.length * 2, true);
+    writeUTFBytes(view, 8, 'WAVE');
+    // FMT sub-chunk
+    writeUTFBytes(view, 12, 'fmt ');
+    view.setUint32(16, 16, true);
+    view.setUint16(20, 1, true);
+    // stereo (2 channels)
+    view.setUint16(22, 2, true);
+    view.setUint32(24, 44100, true);
+    view.setUint32(28, 44100 * 4, true);
+    view.setUint16(32, 4, true);
+    view.setUint16(34, 16, true);
+    // data sub-chunk
+    writeUTFBytes(view, 36, 'data');
+    view.setUint32(40, interleaved.length * 2, true);
+    // write the PCM samples
+    var lng = interleaved.length;
+    var index = 44;
+    var volume = 1;
+    for (var i = 0; i < lng; i++) {
+      view.setInt16(index, interleaved[i] * (32767 * volume), true);
+      index += 2;
+    }
+    p5.prototype.writeFile([view], name, 'wav');
+  };
+  // helper methods to save waves
+  function interleave(leftChannel, rightChannel) {
+    var length = leftChannel.length + rightChannel.length;
+    var result = new Float32Array(length);
+    var inputIndex = 0;
+    for (var index = 0; index < length;) {
+      result[index++] = leftChannel[inputIndex];
+      result[index++] = rightChannel[inputIndex];
+      inputIndex++;
+    }
+    return result;
+  }
+  function writeUTFBytes(view, offset, string) {
+    var lng = string.length;
+    for (var i = 0; i < lng; i++) {
+      view.setUint8(offset + i, string.charCodeAt(i));
+    }
+  }
+}(sndcore, master);
+var peakdetect;
+peakdetect = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+   *  <p>PeakDetect works in conjunction with p5.FFT to
+   *  look for onsets in some or all of the frequency spectrum.
+   *  </p>
+   *  <p>
+   *  To use p5.PeakDetect, call <code>update</code> in the draw loop
+   *  and pass in a p5.FFT object.
+   *  </p>
+   *  <p>
+   *  You can listen for a specific part of the frequency spectrum by
+   *  setting the range between <code>freq1</code> and <code>freq2</code>.
+   *  </p>
+   *
+   *  <p><code>threshold</code> is the threshold for detecting a peak,
+   *  scaled between 0 and 1. It is logarithmic, so 0.1 is half as loud
+   *  as 1.0.</p>
+   *
+   *  <p>
+   *  The update method is meant to be run in the draw loop, and
+   *  <b>frames</b> determines how many loops must pass before
+   *  another peak can be detected.
+   *  For example, if the frameRate() = 60, you could detect the beat of a
+   *  120 beat-per-minute song with this equation:
+   *  <code> framesPerPeak = 60 / (estimatedBPM / 60 );</code>
+   *  </p>
+   *
+   *  <p>
+   *  Based on example contribtued by @b2renger, and a simple beat detection
+   *  explanation by <a
+   *  href="http://www.airtightinteractive.com/2013/10/making-audio-reactive-visuals/"
+   *  target="_blank">Felix Turner</a>.
+   *  </p>
+   *  
+   *  @class  p5.PeakDetect
+   *  @constructor
+   *  @param {Number} [freq1]     lowFrequency - defaults to 20Hz
+   *  @param {Number} [freq2]     highFrequency - defaults to 20000 Hz
+   *  @param {Number} [threshold] Threshold for detecting a beat between 0 and 1
+   *                            scaled logarithmically where 0.1 is 1/2 the loudness
+   *                            of 1.0. Defaults to 0.35.
+   *  @param {Number} [framesPerPeak]     Defaults to 20.
+   *  @example
+   *  <div><code>
+   *  
+   *  var cnv, soundFile, fft, peakDetect;
+   *  var ellipseWidth = 10;
+   *
+   *  function setup() {
+   *    background(0);
+   *    noStroke();
+   *    fill(255);
+   *    textAlign(CENTER);
+   *
+   *    soundFile = loadSound('assets/beat.mp3');
+   *
+   *    // p5.PeakDetect requires a p5.FFT
+   *    fft = new p5.FFT();
+   *    peakDetect = new p5.PeakDetect();
+   *
+   *  }
+   *
+   *  function draw() {
+   *    background(0);
+   *    text('click to play/pause', width/2, height/2);
+   *
+   *    // peakDetect accepts an fft post-analysis
+   *    fft.analyze();
+   *    peakDetect.update(fft);
+   *
+   *    if ( peakDetect.isDetected ) {
+   *      ellipseWidth = 50;
+   *    } else {
+   *      ellipseWidth *= 0.95;
+   *    }
+   *
+   *    ellipse(width/2, height/2, ellipseWidth, ellipseWidth);
+   *  }
+   *
+   *  // toggle play/stop when canvas is clicked
+   *  function mouseClicked() {
+   *    if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+   *      if (soundFile.isPlaying() ) {
+   *        soundFile.stop();
+   *      } else {
+   *        soundFile.play();
+   *      }
+   *    }
+   *  }
+   *  </code></div>
+   */
+  p5.PeakDetect = function (freq1, freq2, threshold, _framesPerPeak) {
+    var framesPerPeak;
+    // framesPerPeak determines how often to look for a beat.
+    // If a beat is provided, try to look for a beat based on bpm
+    this.framesPerPeak = _framesPerPeak || 20;
+    this.framesSinceLastPeak = 0;
+    this.decayRate = 0.95;
+    this.threshold = threshold || 0.35;
+    this.cutoff = 0;
+    // how much to increase the cutoff 
+    // TO DO: document this / figure out how to make it accessible
+    this.cutoffMult = 1.5;
+    this.energy = 0;
+    this.penergy = 0;
+    // TO DO: document this property / figure out how to make it accessible
+    this.currentValue = 0;
+    /**
+     *  isDetected is set to true when a peak is detected.
+     *  
+     *  @attribute isDetected
+     *  @type {Boolean}
+     *  @default  false
+     */
+    this.isDetected = false;
+    this.f1 = freq1 || 40;
+    this.f2 = freq2 || 20000;
+    // function to call when a peak is detected
+    this._onPeak = function () {
+    };
+  };
+  /**
+   *  The update method is run in the draw loop.
+   *
+   *  Accepts an FFT object. You must call .analyze()
+   *  on the FFT object prior to updating the peakDetect
+   *  because it relies on a completed FFT analysis.
+   *
+   *  @method  update
+   *  @param  {p5.FFT} fftObject A p5.FFT object
+   */
+  p5.PeakDetect.prototype.update = function (fftObject) {
+    var nrg = this.energy = fftObject.getEnergy(this.f1, this.f2) / 255;
+    if (nrg > this.cutoff && nrg > this.threshold && nrg - this.penergy > 0) {
+      // trigger callback
+      this._onPeak();
+      this.isDetected = true;
+      // debounce
+      this.cutoff = nrg * this.cutoffMult;
+      this.framesSinceLastPeak = 0;
+    } else {
+      this.isDetected = false;
+      if (this.framesSinceLastPeak <= this.framesPerPeak) {
+        this.framesSinceLastPeak++;
+      } else {
+        this.cutoff *= this.decayRate;
+        this.cutoff = Math.max(this.cutoff, this.threshold);
+      }
+    }
+    this.currentValue = nrg;
+    this.penergy = nrg;
+  };
+  /**
+   *  onPeak accepts two arguments: a function to call when
+   *  a peak is detected. The value of the peak,
+   *  between 0.0 and 1.0, is passed to the callback.
+   *   
+   *  @method  onPeak
+   *  @param  {Function} callback Name of a function that will
+   *                              be called when a peak is
+   *                              detected.
+   *  @param  {Object}   [val]    Optional value to pass
+   *                              into the function when
+   *                              a peak is detected.
+   *  @example
+   *  <div><code>
+   *  var cnv, soundFile, fft, peakDetect;
+   *  var ellipseWidth = 0;
+   *  
+   *  function setup() {
+   *    cnv = createCanvas(100,100);
+   *    textAlign(CENTER);
+   *  
+   *    soundFile = loadSound('assets/beat.mp3');
+   *    fft = new p5.FFT();
+   *    peakDetect = new p5.PeakDetect();
+   *  
+   *    setupSound();
+   *  
+   *    // when a beat is detected, call triggerBeat()
+   *    peakDetect.onPeak(triggerBeat);
+   *  }
+   *  
+   *  function draw() {
+   *    background(0);
+   *    fill(255);
+   *    text('click to play', width/2, height/2);
+   *  
+   *    fft.analyze();
+   *    peakDetect.update(fft);
+   *  
+   *    ellipseWidth *= 0.95;
+   *    ellipse(width/2, height/2, ellipseWidth, ellipseWidth);
+   *  }
+   *  
+   *  // this function is called by peakDetect.onPeak
+   *  function triggerBeat() {
+   *    ellipseWidth = 50;
+   *  }
+   *  
+   *  // mouseclick starts/stops sound
+   *  function setupSound() {
+   *    cnv.mouseClicked( function() {
+   *      if (soundFile.isPlaying() ) {
+   *        soundFile.stop();
+   *      } else {
+   *        soundFile.play();
+   *      }
+   *    });
+   *  }
+   *  </code></div>
+   */
+  p5.PeakDetect.prototype.onPeak = function (callback, val) {
+    var self = this;
+    self._onPeak = function () {
+      callback(self.energy, val);
+    };
+  };
+}(master);
+var gain;
+gain = function () {
+  'use strict';
+  var p5sound = master;
+  /**
+  *  A gain node is usefull to set the relative volume of sound.
+  *  It's typically used to build mixers.   
+  *
+  *  @class p5.Gain
+  *  @constructor
+  *  @example
+  *  <div><code>
+  *
+  * // load two soundfile and crossfade beetween them
+  * var sound1,sound2;
+  * var gain1, gain2, gain3;
+  * 
+  * function preload(){
+  *   soundFormats('ogg', 'mp3');
+  *   sound1 = loadSound('../_files/Damscray_-_Dancing_Tiger_01');
+  *   sound2 = loadSound('../_files/beat.mp3');
+  * }
+  *
+  * function setup() {
+  *   createCanvas(400,200);
+  *
+  *   // create a 'master' gain to which we will connect both soundfiles
+  *   gain3 = new p5.Gain();
+  *   gain3.connect();
+  *
+  *   // setup first sound for playing
+  *   sound1.rate(1);
+  *   sound1.loop();
+  *   sound1.disconnect(); // diconnect from p5 output
+  *
+  *   gain1 = new p5.Gain(); // setup a gain node
+  *   gain1.setInput(sound1); // connect the first sound to its input
+  *   gain1.connect(gain3); // connect its output to the 'master'
+  *
+  *   sound2.rate(1);
+  *   sound2.disconnect();
+  *   sound2.loop();
+  *
+  *   gain2 = new p5.Gain();
+  *   gain2.setInput(sound2);
+  *   gain2.connect(gain3);
+  *
+  * }
+  *
+  * function draw(){
+  *   background(180);
+  *  
+  *   // calculate the horizontal distance beetween the mouse and the right of the screen
+  *   var d = dist(mouseX,0,width,0);
+  *
+  *   // map the horizontal position of the mouse to values useable for volume control of sound1
+  *   var vol1 = map(mouseX,0,width,0,1); 
+  *   var vol2 = 1-vol1; // when sound1 is loud, sound2 is quiet and vice versa
+  *
+  *   gain1.amp(vol1,0.5,0);
+  *   gain2.amp(vol2,0.5,0);
+  *
+  *   // map the vertical position of the mouse to values useable for 'master volume control'
+  *   var vol3 = map(mouseY,0,height,0,1); 
+  *   gain3.amp(vol3,0.5,0);
+  * }
+  *</code></div>
+  *
+  */
+  p5.Gain = function () {
+    this.ac = p5sound.audiocontext;
+    this.input = this.ac.createGain();
+    this.output = this.ac.createGain();
+    // otherwise, Safari distorts
+    this.input.gain.value = 0.5;
+    this.input.connect(this.output);
+    // add  to the soundArray
+    p5sound.soundArray.push(this);
+  };
+  /**
+   *  Connect a source to the gain node.
+   *  
+   *  @method  setInput
+   *  @param  {Object} src     p5.sound / Web Audio object with a sound
+   *                           output.
+   */
+  p5.Gain.prototype.setInput = function (src) {
+    src.connect(this.input);
+  };
+  /**
+   *  Send output to a p5.sound or web audio object
+   *  
+   *  @method  connect
+   *  @param  {Object} unit
+   */
+  p5.Gain.prototype.connect = function (unit) {
+    var u = unit || p5.soundOut.input;
+    this.output.connect(u.input ? u.input : u);
+  };
+  /**
+   *  Disconnect all output.
+   *  
+   *  @method disconnect
+   */
+  p5.Gain.prototype.disconnect = function () {
+    this.output.disconnect();
+  };
+  /**
+   *  Set the output level of the gain node.
+   *  
+   *  @method  amp
+   *  @param  {Number} volume amplitude between 0 and 1.0
+   *  @param  {Number} [rampTime] create a fade that lasts rampTime 
+   *  @param  {Number} [timeFromNow] schedule this event to happen
+   *                                seconds from now
+   */
+  p5.Gain.prototype.amp = function (vol, rampTime, tFromNow) {
+    var rampTime = rampTime || 0;
+    var tFromNow = tFromNow || 0;
+    var now = p5sound.audiocontext.currentTime;
+    var currentVol = this.output.gain.value;
+    this.output.gain.cancelScheduledValues(now);
+    this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
+    this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
+  };
+  p5.Gain.prototype.dispose = function () {
+    // remove reference from soundArray
+    var index = p5sound.soundArray.indexOf(this);
+    p5sound.soundArray.splice(index, 1);
+    this.output.disconnect();
+    this.input.disconnect();
+    this.output = undefined;
+    this.input = undefined;
+  };
+}(master, sndcore);
+var src_app;
+src_app = function () {
+  'use strict';
+  var p5SOUND = sndcore;
+  return p5SOUND;
+}(sndcore, master, helpers, errorHandler, panner, soundfile, amplitude, fft, signal, oscillator, env, pulse, noise, audioin, filter, delay, reverb, metro, looper, soundRecorder, peakdetect, gain);
+}));
\ No newline at end of file
diff --git a/examples/p5js/p5js_sketches/grid24x8_clouds.js b/examples/p5js/p5js_sketches/grid24x8_clouds.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c60aebcd421acf1780c2e19a4baa7cef155d6a3
--- /dev/null
+++ b/examples/p5js/p5js_sketches/grid24x8_clouds.js
@@ -0,0 +1,77 @@
+// Connect to the local instance of fcserver
+var WebSocketAddress = "ws://127.0.0.1:7890"; 
+//Show LED pixel locations.
+var showPixelLocations = true; 
+//Change the HTML Id of the canvas.
+var canvasId = "grid24x8_clouds"
+
+
+var dx = 0;
+var dy = 0;
+var im;
+var d = 0;
+function setup(){
+	var canvas = createCanvas(52, 24);
+	canvas.id(canvasId);
+	socketSetup(WebSocketAddress); // Connect to the local instance of fcserver via websocket.
+	
+	
+	loadPixels();
+	pixelDensity(1);
+	d = pixelDensity();
+
+	// Map an 8x8 grid of LEDs to the center of the window, scaled to take up most of the space
+	var spacing = (height / 8.0)-1;
+	ledGrid8x8(0, width/2, height/2, spacing, 0, true);
+
+	//Put two more 8x8 grids to the left and to the right of that one.
+	ledGrid8x8(64, width/2 - spacing * 8, height/2, spacing, 0, true);
+	ledGrid8x8(128, width/2 + spacing * 8, height/2, spacing, 0, true);
+	colorMode(HSB, 100);
+	background(0);
+}
+
+var noiseScale=0.02;
+
+function fractalNoise(x, y, z) {
+	var r = 0;
+	var amp = 1.0;
+	for (var octave = 0; octave < 4; octave++) {
+		r += noise(x, y, z) * amp;
+		amp /= 2;
+		x *= 2;
+		y *= 2;
+		z *= 2;
+	}
+	return r;
+}
+
+function draw() {
+	var now = millis();
+	var speed = 0.002;
+	var angle = sin(now * 0.001);
+	var z = now * 0.00008;
+	var Hue = now * 0.01;
+	var Scale = 0.005;
+	dx += cos(angle) * speed;
+	dy += sin(angle) * speed;
+	loadPixels();
+	for (var x=0; x < width*d*4; x+=4) {
+		for (var y=0; y < height*d*4; y+=4) {
+			var m = fractalNoise(dx + x*Scale, dy + y*Scale, z+10) - 0.75;
+			var n = fractalNoise(dx + x*Scale, dy + y*Scale, z)*3 - 0.75*3;
+			var c = color(
+			(Hue + 80.0 * m) % 100.0,
+			100 - 100 * constrain(pow(n, 3), 0, .9),
+			100 * constrain(pow(n, 1.5), 0, .9)
+			);
+			var idx = width*d*y+x;
+			pixels[idx] = c._getRed();
+			pixels[idx+1] = c._getGreen();
+			pixels[idx+2] = c._getBlue();
+			//pixels[idx+3] = c._getAlpha();
+		}
+	}
+	updatePixels();
+	drawFrame();
+}
\ No newline at end of file
diff --git a/examples/p5js/p5js_sketches/grid8x8_dot.js b/examples/p5js/p5js_sketches/grid8x8_dot.js
new file mode 100644
index 0000000000000000000000000000000000000000..921541258f2d635c8a11d8132c1e921b014cff4f
--- /dev/null
+++ b/examples/p5js/p5js_sketches/grid8x8_dot.js
@@ -0,0 +1,29 @@
+// Connect to the local instance of fcserver
+var WebSocketAddress = "ws://127.0.0.1:7890"; 
+//Show LED pixel locations.
+var showPixelLocations = true; 
+//Change the HTML Id of the canvas.
+var canvasId = "grid8x8_dot";
+
+
+//Canvas
+function setup() {
+	var canvas = createCanvas(640, 360);
+	canvas.id(canvasId);
+	socketSetup(WebSocketAddress);
+	
+  // Load a sample image
+  dot = loadImage("images/dot.png");
+
+  // Map an 8x8 grid of LEDs to the center of the window
+  ledGrid8x8(0, width/2, height/2, height/12, 0, false);
+}
+
+function draw() {
+  background(0);
+
+  // Draw the image, centered at the mouse location
+  var dotSize = height * 0.7;
+  image(dot, mouseX - dotSize/2, mouseY - dotSize/2, dotSize, dotSize);
+  drawFrame();
+}
\ No newline at end of file
diff --git a/examples/p5js/p5js_sketches/grid8x8_orbits.js b/examples/p5js/p5js_sketches/grid8x8_orbits.js
new file mode 100644
index 0000000000000000000000000000000000000000..c02abb95f464ee428146bdcfb8093d761411f41c
--- /dev/null
+++ b/examples/p5js/p5js_sketches/grid8x8_orbits.js
@@ -0,0 +1,45 @@
+// Connect to the local instance of fcserver. You can change this line to connect to another computer's fcserver
+var WebSocketAddress = "ws://127.0.0.1:7890"; 
+//Show LED pixel locations.
+var showPixelLocations = true; 
+//Change the HTML Id of the canvas.
+var canvasId = "grid8x8_orbits";
+
+
+var dot1; 
+var dot2;
+var px = 0;
+var py = 0;
+function setup(){
+	var canvas = createCanvas(640, 360);
+	canvas.id(canvasId);
+	var spacing = height / 14.0;	
+	socketSetup(WebSocketAddress);	
+	dot1 = loadImage("images/greenDot.png");
+	dot2 = loadImage("images/purpleDot.png");
+	//var spacing = (canvas.height/14);
+	//Connect to the local instance of fcserver
+	//Map an 8x8 grid of LEDs to the center of the window, scaled to take up most of the space
+	ledGrid8x8(0, width/2, height/2, spacing, HALF_PI, false);
+}
+
+function draw(){
+	background(0);
+	blendMode(ADD);
+	// Smooth out the mouse location
+	px += (mouseX - px * 0.2);
+	py += (mouseY - py * 0.2);
+	var a = (millis() * 0.001);
+	var r = (py * 0.5);
+	var dotSize = (r * 4);
+	var dx = (width/2 + cos(a) * r);
+	var dy = (height/2 + sin(a) * r);
+	
+	// Draw it centered at the mouse location
+	image(dot1, dx - dotSize/2, dy - dotSize/2, dotSize, dotSize);
+	
+	// Another dot, mirrored around the center
+	image(dot2, width - dx - dotSize/2, height - dy - dotSize/2, dotSize, dotSize);
+	
+	drawFrame();
+}
\ No newline at end of file
diff --git a/examples/p5js/p5js_sketches/grid8x8_simple_noise.js b/examples/p5js/p5js_sketches/grid8x8_simple_noise.js
new file mode 100644
index 0000000000000000000000000000000000000000..8cdf5262ec430cd038f5bfbbf212f67f8fcfd3e3
--- /dev/null
+++ b/examples/p5js/p5js_sketches/grid8x8_simple_noise.js
@@ -0,0 +1,41 @@
+// Connect to the local instance of fcserver
+var WebSocketAddress = "ws://127.0.0.1:7890"; 
+//Show LED pixel locations.
+var showPixelLocations = true; 
+//Change the HTML Id of the canvas.
+var canvasId = "grid8x8_simple_noise";
+
+
+//A simple example of using Processing's noise() function to draw LED clouds
+var clouds;
+//
+function setup(){
+createCanvas(128, 128);
+socketSetup(WebSocketAddress);
+ledGrid8x8(0, width/2, height/2, height / 8.0, 0, false);
+colorMode(HSB, 100);
+noiseDetail(5, 0.4);
+// Render the noise to a smaller image, it's faster than updating the entire window.
+clouds = new p5.Image(128, 128, RGB);
+}
+
+function draw(){
+	var colorHue = (noise(millis() * 0.0001) * 200) % 100;
+	var z = millis() * 0.0001;
+	var dx = millis() * 0.0001;
+	clouds.loadPixels();
+	for (var x=0; x < clouds.width*4; x+=2) {
+		for (var y=0; y < clouds.height*4; y+=4) {
+			var n = 500 * (noise(dx + x * 0.01, y * 0.01, z) - 0.4);
+			//var c = color(hue, 80 - n, n);
+			clouds.pixels[((x + clouds.width*y))] = colorHue;
+			clouds.pixels[((x + clouds.width*y))+1] = 80 - n;
+			clouds.pixels[((x + clouds.width*y))+2] = n;
+		}
+	}
+	clouds.updatePixels();
+	image(clouds, 0, 0, clouds.width, clouds.height,0,0,width,height);
+	
+	
+	drawFrame();
+}
\ No newline at end of file
diff --git a/examples/p5js/p5js_sketches/strip64_flames.js b/examples/p5js/p5js_sketches/strip64_flames.js
new file mode 100644
index 0000000000000000000000000000000000000000..050aa10a81fa940ea918d01a2b5a931e072fea45
--- /dev/null
+++ b/examples/p5js/p5js_sketches/strip64_flames.js
@@ -0,0 +1,39 @@
+// Connect to the local instance of fcserver
+var WebSocketAddress = "ws://127.0.0.1:7890"; 
+//Show LED pixel locations.
+var showPixelLocations = true; 
+//Change the HTML Id of the canvas.
+var canvasId = "strip64_flames"
+
+
+var im;
+function setup(){
+	var canvas = createCanvas(800, 200);
+	canvas.id(canvasId);
+	socketSetup(WebSocketAddress); // Connect to the local instance of fcserver via websocket.
+	
+	
+	im = loadImage("images/flames.jpeg"); // Load a sample image	
+	ledStrip(0, 64, width/2, height/2, width/70, 0, false);
+	//ledRing(0, 120, width/2, height/2, height/4, 0);
+	//ledGrid8x8(0, width/2, height/2, 5, 0, true);
+	//ledGrid(0, 15, 8, width/2, height/2, 2, 2, 0, true);
+	frameRate(60);
+}
+
+function draw() {
+  
+	// Scale the image so that it matches the width of the window
+	var imHeight = im.height * width / im.width;
+
+	// Scroll down slowly, and wrap around
+	var speed = 0.05;
+	var y = (millis() * -speed) % imHeight;
+  
+	// Use two copies of the image, so it seems to repeat infinitely  
+	image(im, 0, y, width, imHeight);
+	image(im, 0, y + imHeight, width, imHeight);
+  
+	//Send to fcServer.
+	drawFrame();
+}
\ No newline at end of file
diff --git a/examples/p5js/strip64_flames.html b/examples/p5js/strip64_flames.html
new file mode 100644
index 0000000000000000000000000000000000000000..a51ed7562613eee08bc6b6628683b83d4c18d32f
--- /dev/null
+++ b/examples/p5js/strip64_flames.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>strip64_flames</title>
+    <script src="libraries/p5.js" type="text/javascript"></script>
+    <script src="libraries/p5.dom.js" type="text/javascript"></script>
+    <script src="libraries/p5.sound.js" type="text/javascript"></script>
+		<script src="libraries/opc.js" type="text/javascript"></script>
+    <script src="p5js_sketches/strip64_flames.js" type="text/javascript"></script>
+
+    <style> body {padding: 0; margin: 0;} canvas {vertical-align: top;} </style>
+  </head>
+  <body>
+  </body>
+</html>
\ No newline at end of file