diff --git a/jsketch.svg.js b/jsketch.svg.js
new file mode 100644
index 0000000..abbb378
--- /dev/null
+++ b/jsketch.svg.js
@@ -0,0 +1,244 @@
+/*!
+ * jSketch SVG 1.0 | Luis A. Leiva | MIT license
+ */
+
+// XXX: Requires `jsketch.js` to be loaded first.
+
+/**
+ * SVG serializer for the jSketch lib.
+ * @version 1.0
+ * @author Luis A. Leiva
+ * @license MIT license
+ */
+;(function(window) {
+ /**
+ * Convert jSketch canvas to SVG.
+ * @param {function} callback - Callback function, executed when SVG is ready.
+ * @return {string}
+ * @memberof jSketch
+ */
+ window.jSketch.prototype.toSVG = function(callback) {
+ // Save pointer for use in closures.
+ var self = this;
+ // Gather lines together until path is closed.
+ var paths = [];
+ // TODO: Save composite operations. See https://www.w3.org/TR/2002/WD-SVG11-20020215/masking.html
+ var comps = {};
+ // Flag async stuff, like image loading.
+ var asyncOps = 0;
+ // Process jSketch properties.
+ var graphics = {};
+ // Process jSketch methods.
+ var methods = {
+ /**
+ * Add async flag.
+ * @return {string}
+ */
+ addAsync: function() {
+ asyncOps++;
+ return '';
+ },
+ /**
+ * Remove async flag.
+ * @return {string}
+ */
+ removeAsync: function() {
+ asyncOps--;
+ return '';
+ },
+ /**
+ * Serialize image.
+ * @param {object} img - Image element.
+ * @param {number} [x] - Horizontal coordinate.
+ * @param {number} [y] - Vertical coordinate.
+ * @return {string}
+ */
+ drawImage: function(img, x, y) {
+ return '';
+ },
+ /**
+ * Serialize filled rectangle.
+ * @param {number} x - Horizontal coordinate.
+ * @param {number} y - Vertical coordinate.
+ * @param {number} width - Rectangle width.
+ * @param {number} height - Rectangle height.
+ * @return {string}
+ */
+ fillRect: function(x, y, width, height) {
+ return '';
+ },
+ /**
+ * Serialize stroked rectangle.
+ * @param {number} x - Horizontal coordinate.
+ * @param {number} y - Vertical coordinate.
+ * @param {number} width - Rectangle width.
+ * @param {number} height - Rectangle height.
+ * @return {string}
+ */
+ strokeRect: function(x, y, width, height) {
+ return '';
+ },
+ /**
+ * Serialize stroked circle.
+ * @param {number} x - Horizontal coordinate.
+ * @param {number} y - Vertical coordinate.
+ * @param {number} radius - Circle radius.
+ * @return {string}
+ */
+ strokeCircle: function(x, y, radius) {
+ return '';
+ },
+ /**
+ * Serialize filled circle.
+ * @param {number} x - Horizontal coordinate.
+ * @param {number} y - Vertical coordinate.
+ * @param {number} radius - Circle radius.
+ * @return {string}
+ */
+ fillCircle: function(x, y, radius) {
+ return '';
+ },
+ /**
+ * Mark start of path.
+ * @return {string}
+ */
+ beginPath: function() {
+ paths = [];
+ return '';
+ },
+ /**
+ * Mark end of path. Actually serializes the path.
+ * @return {string}
+ */
+ closePath: function() {
+ var path = '';
+ if (paths.length > 0) {
+ path = '';
+ paths = [];
+ }
+ return path;
+ },
+ /**
+ * Add point origin to path.
+ * @param {number} x - Horizontal coordinate.
+ * @param {number} y - Vertical coordinate.
+ * @return {string}
+ */
+ moveTo: function(x, y) {
+ paths.push('M '+ x +' '+ y);
+ return '';
+ },
+ /**
+ * Add line to path.
+ * @param {number} x - Horizontal coordinate.
+ * @param {number} y - Vertical coordinate.
+ * @return {string}
+ */
+ lineTo: function(x, y) {
+ paths.push('L '+ x +' '+ y);
+ return '';
+ },
+ /**
+ * Add curve to path.
+ * @param {number} cpx - Horizontal coordinate of control point.
+ * @param {number} cpy - Vertical coordinate of control point.
+ * @param {number} x - Horizontal coordinate.
+ * @param {number} y - Vertical coordinate.
+ * @return {string}
+ */
+ quadraticCurveTo: function(cpx, cpy, x, y) {
+ paths.push('Q '+ cpx +' '+ cpy + ' '+ x +' '+ y);
+ return '';
+ },
+ };
+
+ function build() {
+ var svg = '';
+ svg += '';
+ svg += '';
+ // Normalize whitespacing.
+ return svg.replace(/\s+/g, ' ');
+ }
+
+ function processElements() {
+ var ret = '';
+ for (var i = 0; i < self.callStack.length; i++) {
+ var entry = self.callStack[i];
+ if (entry.property && typeof entry.value !== 'object') {
+ // Save properties for later processing.
+ graphics[entry.property] = entry.value;
+ } else if (entry.method) {
+ // Ensure method.
+ if (!methods[entry.method]) {
+ console.warn('Method not implemented:', entry.method);
+ continue;
+ }
+ // Process method.
+ ret += methods[entry.method].apply(null, entry.args);
+ } else {
+ console.warn('Unknown call:', entry);
+ }
+ }
+ return ret;
+ }
+
+ // Throttle svg readiness.
+ var timer = setInterval(checkReady, 150);
+ function checkReady() {
+ if (asyncOps <= 0) {
+ clearInterval(timer);
+ var contents = build();
+ callback(contents);
+ } else {
+ console.info('Waiting for queue to be empty:', asyncQueue);
+ }
+ }
+ };
+
+})(this);