Live demo: msurguy.github.io/mosaic-js
Browser-only port of the Python qd_mosaic pipeline: builds photomosaic-style
mosaics out of Quick, Draw! doodles and
exports a pen-plotter-ready SVG. No Python, no gsutil, no intermediate files —
everything (download, thumbnail rasterization, tile matching, SVG generation)
happens in the browser.
npm install
npm run dev # http://localhost:5173
npm run build # static build in dist/, deployable anywhere
Every push to main is built and published to GitHub Pages by
.github/workflows/deploy.yml. The Vite base
defaults to /mosaic-js/ for Pages; set BASE_PATH=/ (or another path) when
building for a different host.
The category-preview atlas (public/category-atlas.png + .json) ships with
the repo. To regenerate it (e.g. after changing tile size or picking logic):
node scripts/build-atlas.mjs
The script is dependency-free: it range-fetches ~16 KB per category, picks the fullest recognized doodle among the first few, rasterizes the strokes itself, and hand-encodes the grayscale PNG with node's built-in zlib.
- Drawings — hit Browse categories for a full-screen searchable grid of
all 345 Quick Draw categories, each with a doodle preview (hover a card to
see a dozen live samples), and hit Load drawings. Each selected
category gets its own count for weighted mixes, and unrecognized doodles
can be filtered out (the
recognizedflag in the binary format). Bin files are streamed straight from the Quick Draw GCS bucket (via the JSON API endpoint, which allows CORS) and the download is aborted as soon as enough drawings have arrived. Downloads are cached in the browser (Cache API), so re-loads are instant. You can also drop local.binfiles (e.g. from the repo'sbdata/) instead — works offline. - Target picture — drop or browse for any image, then tune brightness / contrast / gamma. The thumbnail preview uses the same lookup table as the matcher, so what you see is what gets matched.
- Settings — same knobs as
build_mosaic.py: max tiles, match resolution, duplicate distance, mirrored tiles (-noflops), repeats (-nodupes), inverted white-ink-on-black thumbnails (make_thumbs.py -inv), and draft/normal/accurate matching (-draft/-accurate). - Build — matching runs in a Web Worker; a 2,000-image / 800-tile build takes well under a second.
- Plot — pick a paper size (A3/A4/A5/Letter/custom, in real millimetres),
margins, and orientation; the mosaic is scaled to fit and centered, and the
SVG carries proper
mmunits. Output can be split into Inkscape-style pen layers (2–3 by target tone quantiles, or one per category) that vpype and AxiDraw tooling understand. Plot ordering keeps the original conventions: the four corner cells first to catch a skewed sheet, then serpentine rows.
| Module | Ports |
|---|---|
src/quickdraw.js |
get_bins.py + the binary parser from make_thumbs.py |
src/rasterize.js |
make_thumbs.py (in-memory, no PNG files) |
src/mosaic-worker.js |
mosaick.py selection algorithm (luminance space) |
src/preview.js |
the JPG render from mosaick.py makeMosaic |
src/svg.js |
render_mosaic_svg.py |
Not ported: hmode (marked "not yet working" upstream), crop variations
(Quick Draw thumbs are square; the reference workflow used -novars),
Flickr sets, heatmap, and the mixin/tint raster options.