diff --git a/core/src/processing/awt/PShapeJava2D.java b/core/src/processing/awt/PShapeJava2D.java
index 2b9b968a4c..42ced54399 100644
--- a/core/src/processing/awt/PShapeJava2D.java
+++ b/core/src/processing/awt/PShapeJava2D.java
@@ -33,6 +33,9 @@
 import java.awt.image.Raster;
 import java.awt.image.WritableRaster;
 
+import java.util.Arrays;
+import java.awt.Color;
+
 import processing.core.PApplet;
 import processing.core.PGraphics;
 import processing.core.PShapeSVG;
@@ -96,16 +99,30 @@ public void setColor(String colorText, boolean isFill) {
   */
 
 
-  static class LinearGradientPaint implements Paint {
+  public static class LinearGradientPaint implements Paint {
     float x1, y1, x2, y2;
     float[] offset;
     int[] color;
+    Color[] colors;
     int count;
     float opacity;
+    AffineTransform xform;
+
+
+    public static enum CycleMethod {
+      NO_CYCLE,
+      REFLECT,
+      REPEAT
+    }
+
+    public static enum ColorSpaceType {
+      SRGB,
+      LINEAR_RGB
+    }
 
     public LinearGradientPaint(float x1, float y1, float x2, float y2,
                                float[] offset, int[] color, int count,
-                               float opacity) {
+                               float opacity, AffineTransform xform) {
       this.x1 = x1;
       this.y1 = y1;
       this.x2 = x2;
@@ -114,6 +131,13 @@ public LinearGradientPaint(float x1, float y1, float x2, float y2,
       this.color = color;
       this.count = count;
       this.opacity = opacity;
+      this.xform = xform;
+
+      //set an array of type Color
+      this.colors = new Color[this.color.length];
+      for (int i = 0; i < this.color.length; i++) {
+        this.colors[i] = new Color(this.color[i], true);
+      }
     }
 
     public PaintContext createContext(ColorModel cm,
@@ -125,6 +149,35 @@ public PaintContext createContext(ColorModel cm,
                                        (float) t2.getX(), (float) t2.getY());
     }
 
+    public Point2D getStartPoint() {
+      return new Point2D.Float(this.x1, this.y1);
+    }
+
+    public Point2D getEndPoint() {
+      return new Point2D.Float(this.x2, this.y2);
+    }
+
+    /* MultipleGradientPaint methods... */
+    public AffineTransform getTransform() {
+      return this.xform;
+    }
+
+    public ColorSpaceType getColorSpace() {
+      return ColorSpaceType.SRGB;
+    }
+    
+    public CycleMethod getCycleMethod() {
+      return CycleMethod.NO_CYCLE;
+    }
+
+    public Color[] getColors() {
+        return Arrays.copyOf(this.colors, this.colors.length);
+    }
+
+    public float[] getFractions() {
+        return Arrays.copyOf(this.offset, this.offset.length);
+    }
+
     public int getTransparency() {
       return TRANSLUCENT;  // why not.. rather than checking each color
     }
@@ -221,16 +274,29 @@ public Raster getRaster(int x, int y, int w, int h) {
   }
 
 
-  static class RadialGradientPaint implements Paint {
+  public static class RadialGradientPaint implements Paint {
     float cx, cy, radius;
     float[] offset;
     int[] color;
+    Color[] colors;
     int count;
     float opacity;
+    AffineTransform xform;
+
+    public static enum CycleMethod {
+      NO_CYCLE,
+      REFLECT,
+      REPEAT
+    }
+
+    public static enum ColorSpaceType {
+      SRGB,
+      LINEAR_RGB
+    }
 
     public RadialGradientPaint(float cx, float cy, float radius,
                                float[] offset, int[] color, int count,
-                               float opacity) {
+                               float opacity, AffineTransform xform) {
       this.cx = cx;
       this.cy = cy;
       this.radius = radius;
@@ -238,6 +304,13 @@ public RadialGradientPaint(float cx, float cy, float radius,
       this.color = color;
       this.count = count;
       this.opacity = opacity;
+      this.xform = xform;
+
+      //set an array of type Color
+      this.colors = new Color[this.color.length];
+      for (int i = 0; i < this.color.length; i++) {
+        this.colors[i] = new Color(this.color[i], true);
+      }
     }
 
     public PaintContext createContext(ColorModel cm,
@@ -246,6 +319,41 @@ public PaintContext createContext(ColorModel cm,
       return new RadialGradientContext();
     }
 
+    public Point2D getCenterPoint() {
+      return new Point2D.Double(this.cx, this.cy);
+    }
+
+    //TODO: investigate how to change a focus point for 0% x of the gradient
+    //for now default to center x/y
+    public Point2D getFocusPoint() {
+      return new Point2D.Double(this.cx, this.cy);
+    }
+
+    public float getRadius() {
+        return this.radius;
+    }
+
+    /* MultipleGradientPaint methods... */
+    public AffineTransform getTransform() {
+      return this.xform;
+    }
+    
+    public ColorSpaceType getColorSpace() {
+      return ColorSpaceType.SRGB;
+    }
+    
+    public CycleMethod getCycleMethod() {
+      return CycleMethod.NO_CYCLE;
+    }
+
+    public Color[] getColors() {
+        return Arrays.copyOf(this.colors, this.colors.length);
+    }
+
+    public float[] getFractions() {
+        return Arrays.copyOf(this.offset, this.offset.length);
+    }
+
     public int getTransparency() {
       return TRANSLUCENT;
     }
@@ -305,14 +413,14 @@ protected Paint calcGradientPaint(Gradient gradient) {
       LinearGradient grad = (LinearGradient) gradient;
       return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2,
                                      grad.offset, grad.color, grad.count,
-                                     opacity);
+                                     opacity, grad.transform);
 
     } else if (gradient instanceof RadialGradient) {
 //      System.out.println("creating radial gradient");
       RadialGradient grad = (RadialGradient) gradient;
       return new RadialGradientPaint(grad.cx, grad.cy, grad.r,
                                      grad.offset, grad.color, grad.count,
-                                     opacity);
+                                     opacity, grad.transform);
     }
     return null;
   }
diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java
index e85389f02e..7ffa977491 100644
--- a/core/src/processing/core/PShapeSVG.java
+++ b/core/src/processing/core/PShapeSVG.java
@@ -1273,8 +1273,8 @@ void setFillOpacity(String opacityText) {
     fillColor = ((int) (fillOpacity * 255)) << 24 | fillColor & 0xFFFFFF;
   }
 
-
-  void setColor(String colorText, boolean isFill) {
+  //making this public allows us to set gradient fills on a PShape
+  public void setColor(String colorText, boolean isFill) {
     colorText = colorText.trim();
     int opacityMask = fillColor & 0xFF000000;
     boolean visible = true;
@@ -1497,7 +1497,7 @@ static protected float parseFloatOrPercent(String text) {
 
 
   static public class Gradient extends PShapeSVG {
-    AffineTransform transform;
+    public AffineTransform transform;
 
     public float[] offset;
     public int[] color;
diff --git a/java/libraries/svg/src/processing/svg/GradientExtensionHandler.java b/java/libraries/svg/src/processing/svg/GradientExtensionHandler.java
new file mode 100644
index 0000000000..370adbea93
--- /dev/null
+++ b/java/libraries/svg/src/processing/svg/GradientExtensionHandler.java
@@ -0,0 +1,209 @@
+package processing.svg;
+
+import static org.apache.batik.util.SVGConstants.*;
+
+import processing.awt.PShapeJava2D.LinearGradientPaint;
+import processing.awt.PShapeJava2D.RadialGradientPaint;
+
+import java.awt.Color;
+import java.awt.MultipleGradientPaint;
+import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+
+import java.util.Objects;
+
+import org.apache.batik.svggen.DefaultExtensionHandler;
+import org.apache.batik.svggen.SVGColor;
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGPaintDescriptor;
+import org.w3c.dom.Element;
+
+/**
+ * Extension of Batik's {@link DefaultExtensionHandler} which handles different kinds of Paint objects
+ * based on the extenstion by Martin Steiger https://gist.github.com/msteiger/4509119
+ * modified to work with Processing's SVG export library, by Benjamin Fox https://github.com/tracerstar
+ */
+public class GradientExtensionHandler extends DefaultExtensionHandler {
+
+  @Override
+	public SVGPaintDescriptor handlePaint(Paint paint, SVGGeneratorContext genCtx) {
+
+		// Handle LinearGradientPaint
+		if (paint instanceof LinearGradientPaint) {
+			return getLgpDescriptor((LinearGradientPaint) paint, genCtx);
+		}
+			
+		// Handle RadialGradientPaint
+		if (paint instanceof RadialGradientPaint) {
+			return getRgpDescriptor((RadialGradientPaint) paint, genCtx);
+		}
+		
+		return super.handlePaint(paint, genCtx);
+	}
+
+	private SVGPaintDescriptor getLgpDescriptor(LinearGradientPaint gradient, SVGGeneratorContext genCtx) {
+		Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_LINEAR_GRADIENT_TAG);
+
+		// Create and set unique XML id
+		String id = genCtx.getIDGenerator().generateID("gradient");
+		gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
+
+		// Set x,y pairs
+		Point2D startPt = gradient.getStartPoint();
+		gradElem.setAttribute("x1", String.valueOf(startPt.getX()));
+		gradElem.setAttribute("y1", String.valueOf(startPt.getY()));
+
+		Point2D endPt = gradient.getEndPoint();
+		gradElem.setAttribute("x2", String.valueOf(endPt.getX()));
+		gradElem.setAttribute("y2", String.valueOf(endPt.getY()));
+
+		//TODO: change this to be: addMgpAttributes after refactoring the paint methods
+		addLgpAttributes(gradElem, genCtx, gradient);
+
+		return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
+	}
+
+	private SVGPaintDescriptor getRgpDescriptor(RadialGradientPaint gradient, SVGGeneratorContext genCtx) {
+		Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_RADIAL_GRADIENT_TAG);
+
+		// Create and set unique XML id
+		String id = genCtx.getIDGenerator().generateID("gradient");
+		gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
+
+		// Set x,y pairs
+		Point2D centerPt = gradient.getCenterPoint();
+		gradElem.setAttribute("cx", String.valueOf(centerPt.getX()));
+		gradElem.setAttribute("cy", String.valueOf(centerPt.getY()));
+
+		Point2D focusPt = gradient.getFocusPoint();
+		gradElem.setAttribute("fx", String.valueOf(focusPt.getX()));
+		gradElem.setAttribute("fy", String.valueOf(focusPt.getY()));
+
+		gradElem.setAttribute("r", String.valueOf(gradient.getRadius()));
+
+		//TODO: change this to be: addMgpAttributes after refactoring the paint methods
+		addRgpAttributes(gradElem, genCtx, gradient);
+
+		return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
+	}
+	
+	
+	/*
+		Being lazy here to duplicate the methods so we don't have to refactor the two gradient paints 
+		to implement java.awt.MultipleGradientPaint
+
+		TODO: make the effort to refactor them to properly implement java.awt.MultipleGradientPaint
+	*/
+	private void addLgpAttributes(Element gradElem, SVGGeneratorContext genCtx, LinearGradientPaint gradient) {
+		gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
+
+		// Set cycle method
+		switch (gradient.getCycleMethod()) {
+			case REFLECT:
+				gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REFLECT_VALUE);
+				break;
+			case REPEAT:
+				gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REPEAT_VALUE);
+				break;
+			case NO_CYCLE:
+			default:
+				gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_PAD_VALUE);	// this is the default
+				break;
+		}
+
+		// Set color space
+		switch (gradient.getColorSpace()) {
+			case LINEAR_RGB:
+				gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_LINEAR_RGB_VALUE);
+				break;
+			case SRGB:
+			default:
+				gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_SRGB_VALUE);
+				break;
+		}
+
+		// Set transform matrix if not identity
+		AffineTransform tf = gradient.getTransform();
+		if (!Objects.isNull(tf) && !tf.isIdentity()) {
+			String matrix = "matrix(" + 
+					tf.getScaleX() + " " + tf.getShearX() + " " + tf.getTranslateX() + " " + 
+					tf.getScaleY() + " " + tf.getShearY() + " " + tf.getTranslateY() + ")";
+			gradElem.setAttribute(SVG_TRANSFORM_ATTRIBUTE, matrix);
+		}
+
+		// Convert gradient stops
+		Color[] colors = gradient.getColors();
+		float[] fracs = gradient.getFractions();
+		
+		for (int i = 0; i < colors.length; i++) {
+			Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
+			SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
+
+			stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
+			stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
+
+			if (colors[i].getAlpha() != 255) {
+				stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
+			}
+			
+			gradElem.appendChild(stop);
+		}
+	}
+
+	private void addRgpAttributes(Element gradElem, SVGGeneratorContext genCtx, RadialGradientPaint gradient) {
+		gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
+
+		// Set cycle method
+		switch (gradient.getCycleMethod()) {
+			case REFLECT:
+				gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REFLECT_VALUE);
+				break;
+			case REPEAT:
+				gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REPEAT_VALUE);
+				break;
+			case NO_CYCLE:
+			default:
+				gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_PAD_VALUE);	// this is the default
+				break;
+		}
+
+		// Set color space
+		switch (gradient.getColorSpace()) {
+			case LINEAR_RGB:
+				gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_LINEAR_RGB_VALUE);
+				break;
+			case SRGB:
+			default:
+				gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_SRGB_VALUE);
+				break;
+		}
+
+		// Set transform matrix if not identity
+		AffineTransform tf = gradient.getTransform();
+		if (!Objects.isNull(tf) && !tf.isIdentity()) {
+			String matrix = "matrix(" + 
+					tf.getScaleX() + " " + tf.getShearX() + " " + tf.getTranslateX() + " " + 
+					tf.getScaleY() + " " + tf.getShearY() + " " + tf.getTranslateY() + ")";
+			gradElem.setAttribute(SVG_TRANSFORM_ATTRIBUTE, matrix);
+		}
+
+		// Convert gradient stops
+		Color[] colors = gradient.getColors();
+		float[] fracs = gradient.getFractions();
+		
+		for (int i = 0; i < colors.length; i++) {
+			Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
+			SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
+
+			stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
+			stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
+
+			if (colors[i].getAlpha() != 255) {
+				stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
+			}
+			
+			gradElem.appendChild(stop);
+		}
+	}
+}
diff --git a/java/libraries/svg/src/processing/svg/PGraphicsSVG.java b/java/libraries/svg/src/processing/svg/PGraphicsSVG.java
index 3b170353ba..857f5d93a3 100644
--- a/java/libraries/svg/src/processing/svg/PGraphicsSVG.java
+++ b/java/libraries/svg/src/processing/svg/PGraphicsSVG.java
@@ -87,6 +87,10 @@ public void beginDraw() {
     g2 = new SVGGraphics2D(document);
     ((SVGGraphics2D) g2).setSVGCanvasSize(new Dimension(width, height));
 
+    //set the extension handler to allow linear and radial gradients to be exported as svg
+    GradientExtensionHandler gradH = new GradientExtensionHandler();
+    ((SVGGraphics2D) g2).setExtensionHandler(gradH);
+
     // Done with our work, let's check on defaults and the rest
     //super.beginDraw();
     // Can't call super.beginDraw() because it'll nuke our g2