Turn any photo into a pencil sketch, entirely in your browser. Nothing is uploaded — the image never leaves your device.
- Drag, drop, or browse to load a JPG, PNG, or WEBP.
- Live preview of the original and the sketch, side by side.
- Pencil types: graphite, charcoal, and colored.
- Paper backgrounds: white, cream, transparent, or a custom colour.
- Fine-tuning: stroke intensity, contrast, brightness, and detail.
- Export as PNG, JPG, or WEBP with an adjustable quality.
- Light and dark themes — follows your OS preference by default, with a header toggle that persists your choice.
- Fully client-side — processing runs in a Web Worker, so the UI stays responsive and no data is sent anywhere.
The sketch uses the classic "color dodge" pencil technique (src/sketch/sketch.ts):
- Convert the photo to grayscale and keep its inverse.
- Blur the inverse twice with a fast separable box blur (radius set by Detail).
- Color-dodge blend the grayscale over the blurred inverse to find edges.
- Apply Contrast/Brightness, then a gamma curve (Stroke intensity) to get the per-pixel line amount.
- Composite the pencil ink over the chosen paper (or export alpha for a transparent background).
The algorithm is a pure, DOM-free function that runs both in the Web Worker and in unit tests.
- Vite + React + TypeScript
- CSS Modules with a small design-tokens file (
src/styles/tokens.css) - Web Worker for image processing
- Vitest for unit tests
- ESLint + Prettier
src/
App.tsx App shell + top-level state
sketch/ The pure sketch algorithm and its Web Worker
sketch.ts toSketch(imageData, params) -> imageData
boxBlur.ts separable box blur
sketch.worker.ts runs toSketch off the main thread
types.ts SketchParams and friends
hooks/useSketch.ts debounced worker orchestration
lib/ image loading, colour parsing, download
state/sketchParams.ts params reducer
components/ Stage, Controls, SegmentedControl, Slider, ImageCanvas
styles/ tokens + global styles
npm install # install dependencies
npm run dev # start the dev server
npm test # run unit tests
npm run lint # lint
npm run typecheck
npm run build # production build into dist/
npm run preview # preview the production buildnpm run build produces a static site in dist/ that can be served from any static host.
The Vite base is relative (./), so it works from a sub-path too.
A GitHub Actions workflow (.github/workflows/deploy.yml) publishes to GitHub Pages. To
enable it, go to Settings → Pages → Build and deployment and set the source to
GitHub Actions.
