Skip to content
Back to all work
Application

Numbers

Any picture into a paint-by-numbers sheet, coloured with the pens you actually own.

Year
2026
Status
Live
Role
Solo: image processing engine, application, AI service, deployment.
Numbers

Upload a drawing, an illustration or a photo and get back a print-ready paint-by-numbers sheet: clean outlines, a number in every field, and a colour key. The point is that it gets coloured in, so you can save the palette of your own felt-tips or crayons and the generator will use only those. Prints run from A4 up to large formats, split across A4 or A3 sheets that tape together into one picture.

Stack

  • Next.js 16
  • React 19
  • TypeScript
  • Tailwind CSS
  • Web Workers
  • PostgreSQL
  • Drizzle
  • Better Auth
  • jsPDF
  • Python
  • PyTorch
  • Real-ESRGAN
  • Docker
  • Proxmox

In practice

  • The palette is pulled out of the picture automatically and can be edited, or swapped for the saved set of your own pens.
  • A mode for line art with black outlines, like a printed colouring book, and a separate one for photographs.
  • A suggested number of colours for each picture, so you do not have to guess how many it needs.
  • Export to vector PDF: the sheet, the colour key on a page of its own, and large prints split into sheets to tape together.
  • Accounts with email verification and password reset, and saved projects that open straight away instead of being generated again.
  • Small pictures are upscaled by AI before processing, so a low-resolution image still gives clean fields.

How it is built

  • The image processing engine is my own, about 3,300 lines of TypeScript with no graphics library underneath. It runs entirely in the browser in Web Workers, and the slowest stage is cut into strips and computed in parallel across several cores.
  • Every threshold (smallest field, line weight, digit size) is measured in millimetres on paper rather than in pixels, which is what keeps every print colourable whatever its size.
  • Edge-preserving smoothing first: a bilateral filter in CIELAB removes noise and texture while the edges stay sharp.
  • Black outlines are detected, their gaps closed, and the lines themselves turned into fields of their own, the way printed colouring books do it.
  • Colour quantisation depends on the source. Line art goes through k-means in Lab with the background excluded and small but distinctive colours (eyes, for one) rescued. Photographs are first cut into shapes with SLIC superpixels, similar cells are merged, and only then are colours chosen.
  • Cleanup removes antialiasing halos, stray islands and fields too small to colour, and recognises a light background as blank paper.
  • Field boundaries are traced as a planar graph, smoothed and fitted with Bézier curves that keep sharp corners, so neighbouring fields share exactly the same line.
  • Each number goes at the point of its field furthest from the edge, found with a distance transform.
  • Output is SVG and vector PDF through jsPDF. Projects are stored in a compact binary format of my own, gzipped with the CompressionStream API.
  • Changes to the engine are checked by a regression test: before every deploy it measures colour deviation (ΔE) across a set of test images.
  • Upscaling is a separate Python microservice (Flask, Gunicorn, PyTorch on CPU) running Real-ESRGAN on anything under 1,000 px, reached through an API of my own guarded by a key.
  • Next.js 16 in standalone mode, React 19 with the React Compiler, Tailwind 4, PostgreSQL through Drizzle with migrations run at startup, and Better Auth with Nodemailer for accounts.
  • On my own Proxmox server, with separate containers for the application, the database and the AI service, a persistent volume for project files and a health-check endpoint.

Gallery

projekty

Account

Dark Mode

Let's build something together

Got something in mind? Even half an idea is enough to start a conversation. Every message gets an answer, even when the honest answer is that someone else suits it better.

An answer within 24 hours, with a first estimate and how I would approach it.

Based in Belgium, working remotely with clients in English and Polish.