MAMATHA EPILI / ENGINEERING & AI
Selected work

Open-source toolkit2026Published

image-to-toon

One Canvas engine, three published packages

Stack

Frontend
  • TypeScript across three packages
  • Canvas 2D pixel pipeline, no WebGL dependency
  • React hook and drop-in component
  • Angular standalone component and service
  • Three Vite playgrounds — vanilla, React, Angular
Backend & Data
  • No backend: every pixel is processed in the browser
  • Zero runtime dependencies in the core
  • ESM and CJS builds with full type declarations
  • Angular package built Ivy partial-compiled via ng-packagr
  • Turborepo across an npm and pnpm workspace
  • Pluggable segmentation and AI drivers behind an interface

How it works

Flowchart: An image is validated first — size, MIME type and dimensions — and a bad file fails with a typed error before any pixel work. Good input runs through the Canvas 2D pipeline (Kuwahara smoothing, cel quantisation, Sobel edges), takes a style preset, optionally has its background replaced, and lands back on the canvas. The React hook and Angular component are thin wrappers over the same core.YesNosame coreImage chosen86.5file or canvas sourceValid file?80.5size · MIME ·dimensionsTyped error202.5fails before thepipelineCanvas 2D pipeline80.5Kuwahara · quantise ·SobelApply style preset80.5cartoon · comic ·painting · sketchReplace background80.5SegmentationDriver,optionalToon on canvas86.5everything client-sideFramework bindings202.5useCaricature() ·Angular component
An image is validated first — size, MIME type and dimensions — and a bad file fails with a typed error before any pixel work. Good input runs through the Canvas 2D pipeline (Kuwahara smoothing, cel quantisation, Sobel edges), takes a style preset, optionally has its background replaced, and lands back on the canvas. The React hook and Angular component are thin wrappers over the same core.

Try it here

Live — runs the project’s own code

The published packages, running in your browser. Upload a photo, switch styles, drag the sliders — every pixel is processed on this page, with no upload and no key.

Open the demo in its own tab Served from mamta-epili.github.io, sandboxed.

Problem

Image-effect libraries tend to arrive welded to one framework, or to be a thin wrapper over a hosted API that needs a key and uploads the photo. I wanted the transformation itself to be the library — pure pixels, no network, no account — with the framework bindings as thin adapters rather than separate rewrites.

What I built

  • A framework-agnostic Canvas engine published to npm with zero runtime dependencies: Kuwahara edge-preserving smoothing, luma and chroma cel quantization, colour-aware Sobel edges with a smoothstep ink curve, adaptive thresholding and a radial warp.
  • Four styles — cartoon, comic, painting and sketch — with seven tuned presets and around a dozen live parameters.
  • React and Angular packages over the same core: a `useCaricature()` hook with a drop-in component, and an Angular standalone component with a service.
  • Background replacement — blur, solid, gradient or transparent — with a tunable subject ellipse by default and a `SegmentationDriver` interface for a real cutout.
  • Input validation with typed error codes: size limits, a MIME allow-list and maximum source dimensions, so a bad file fails predictably rather than mid-pipeline.

Technical decisions & trade-offs

  • The core knows nothing about React or Angular. Both bindings are adapters over the same engine, so a fix lands once and a third framework is a new package rather than a fork.
  • Everything runs on the client. No upload, no key, no account — which removes the privacy question entirely for a tool whose input is somebody’s photograph.
  • Heavy dependencies are peer, not bundled: MediaPipe and imgly are optional segmentation drivers behind an interface, so installing the core costs nothing.
  • Canvas 2D rather than WebGL, because the pipeline is filter-bound rather than geometry-bound and it runs everywhere without a context negotiation.
  • The engine returns a canvas, a blob, base64 and FormData, so persisting a result is the caller’s decision rather than the library’s.

Challenges solved

  • Kuwahara smoothing is quadratic in kernel radius; keeping it interactive on a large photo meant working in a downscaled pass and compositing back rather than filtering at full resolution.
  • Three packages, one version: the bindings depend on an exact core version, so a release that updates one and not the others is a broken install rather than a subtle bug.
  • Angular ships Ivy partial-compiled FESM2022 while the others are tsup ESM and CJS — one repository, two build systems, without letting the type surfaces drift apart.

Outcome

Three packages on npm under MIT, and a live playground where the whole pipeline runs in the visitor’s browser with nothing to install and no key to obtain.

Permalink: https://www.mamathaepili.com/projects/image-to-toon