Vectorizer.AI Downloader Tampermonkey A developer known as k33bs published a Tampermonkey userscript that extracts full-resolution SVG output from vectorizer.ai without paying, by hooking the site's in-page VectorImage model rather than intercepting the canvas. The script defines a setter on Object.prototype.isReady to capture the model instance once all result chunks load, then serializes every curve—including off-screen beziers the site's renderer culls—into a downloadable SVG via a cloned download button. | | // ==UserScript== | | | // @name Vectorizer.AI Downloader | | | // @version 0.1.0 | | | // @description Reads the vector result straight from vectorizer.ai's in-page model and downloads it as a full SVG for free. - fix of https://gist.github.com/mateoltd/b976a33bd1c3d106d5c51b3d000a346d | | | // @author k33bs | | | // @namespace http://tampermonkey.net/ | | | // @license MIT | | | // @match https://vectorizer.ai/images/ | | | // @grant none | | | // @run-at document-start | | | // ==/UserScript== | | | function { | | | 'use strict'; | | | console.log 'Vectorizer.AI Downloader: Script injected and waiting for the vector result.' ; | | | / | | | Canvas interception can't work here: the site only draws what's on screen. Loops outside the | | | viewport are skipped, and big shapes get their off-screen beziers drawn as straight lines. | | | So we grab the site's VectorImage model itself and serialize every curve from it. | | | | | | VectorImage sets isReady = true once all result chunks are loaded and committed, | | | so a setter on Object.prototype hands us the instance. | | | / | | | let vectorImage = null; | | | Object.defineProperty Object.prototype, 'isReady', { | | | get { | | | return this. isReady; | | | }, | | | set value { | | | this. isReady = value; | | | if value === true && this.vectorShapes && this.topVectorShapes { | | | vectorImage = this; | | | console.log Vector result captured: ${this.vectorShapes.length} shapes, ${this.imageWidth}x${this.imageHeight} ; | | | createDownloadButton ; | | | } | | | }, | | | configurable: true | | | } ; | | | // UI | | | let customDownloadButton = null; | | | function createDownloadButton { | | | if customDownloadButton return; // Only create it once | | | // We use a MutationObserver to wait for the page's UI to be ready ; | | | const observer = new MutationObserver mutations, obs = { | | | const originalButton = document.querySelector ' App-DownloadLink' ; | | | if originalButton { | | | console.log "Original download button found. Creating custom button." ; | | | customDownloadButton = originalButton.cloneNode true ; // Clone to keep the style | | | customDownloadButton.id = 'custom-download-button'; | | | customDownloadButton.href = ' '; // Clear original link | | | customDownloadButton.style.backgroundColor = ' 5cb85c'; // Green color for distinction | | | const textSpan = customDownloadButton.querySelector '.showPaid' ; | | | if textSpan textSpan.textContent = 'DOWNLOAD SVG'; | | | originalButton.parentNode.insertBefore customDownloadButton, originalButton ; | | | customDownloadButton.addEventListener 'click', e = { | | | e.preventDefault ; | | | e.stopPropagation ; | | | const svgContent = buildSvgFromData ; | | | const blob = new Blob svgContent , { type: 'image/svg+xml;charset=utf-8' } ; | | | const url = URL.createObjectURL blob ; | | | // Set filename from the original, if available | | | let filename = 'vectorized-result.svg'; | | | if window.ResumeImage && window.ResumeImage.originalFilename { | | | filename = window.ResumeImage.originalFilename.replace /\. ^/. +$/, ".svg" ; | | | } | | | // Create a temporary link to trigger download | | | const tempLink = document.createElement 'a' ; | | | tempLink.href = url; | | | tempLink.download = filename; | | | document.body.appendChild tempLink ; | | | tempLink.click ; | | | document.body.removeChild tempLink ; | | | URL.revokeObjectURL url ; | | | }, true ; | | | obs.disconnect ; // Stop observing once the button is created | | | } | | | } ; | | | observer.observe document.body, { childList: true, subtree: true } ; | | | } | | | // SVG Generation | | | / | | | Mirrors the site's VectorImage.drawTo without the viewport culling: | | | top-level gap fillers first, then per shape its fill followed by the gap fillers of its inner loops. | | | / | | | function buildSvgFromData { | | | const vi = vectorImage; | | | const out = ; | | | // Gap fillers: 2px non-scaling strokes with bevel joins, same as the site draws them | | | const strokeInterfaces = list = { | | | for const i of list { | | | if i.shouldDraw continue; | | | const hex = ' ' + i.argbInt & 0xffffff .toString 16 .padStart 6, '0' ; | | | out.push