Cover image for 1,756 hand drawn icons, and nobody drew them

Julien

For a while my routine on side projects was this: find one sketchy icon somewhere, download the SVG, rename it, drop it in assets, then fix its stroke width because it never matched anything else I had. Next project, same thing. The hand drawn sets I could find were small and the glyph I wanted was never in them.

I did that enough times to start wondering why I was doing it by hand at all. The look comes down to a wobble in the line, and a wobble is arithmetic.

Rough.js had worked that out years ago. It draws hand drawn shapes in the browser, and reading how it does it is what stopped me treating the effect as something you need an illustrator for. Bow the straight lines a little, move the endpoints around, and an eye reads that as a hand.

The other half was already solved too. I use Lucide in everything I build. 1,756 glyphs on a 24 unit grid with a 2 pixel stroke, and somebody has already had the argument about what a "database backup" icon should look like. That argument is the hard part of building an icon set, and it was done.

So the plan was to take geometry that already works and add the hand with code.

The generator

@sketchyicons is what came out of that. It reads Lucide's paths, bows every straight run into a quadratic with the control point pushed off the chord, drifts every coordinate by a fraction of a unit, and takes the random sequence for that drift from the icon's own name.

arrow-right in Lucide is M5 12h14. A horizontal line, 14 units long. After the generator it is M5 11.99Q12 11.56 18.81 11.93, a curve whose control point sits four tenths of a unit off the chord. There are 7,027 paths in the catalogue and every one of them goes through that.

Because the sequence comes from the name and not from a clock, two runs of the generator write byte identical files, and there is a test that fails if they ever stop doing that. An icon set that redraws itself on every build would put noise in every diff I ever open, so it was not going to ship that way.

How much hand a coordinate gets depends on where it sits. Short runs move less than long ones. If a subpath is made of straight lines only, a cross or a chevron, it gets held closer to the ruler, and skipping that gives you a circle-plus whose plus looks broken rather than drawn.

Actually using it

npm i @sketchyicons/react

Enter fullscreen mode Exit fullscreen mode

import { House, Star } from "@sketchyicons/react";

<House size={20} />
<Star size={15} color="#2B2521" strokeWidth={1.75} />

Enter fullscreen mode Exit fullscreen mode

The props are Lucide's, size color strokeWidth absoluteStrokeWidth fill, so if Lucide is already in your project the migration is the import line. The 247 names Lucide has renamed over the years are exported next to the current ones, so Home, HelpCircle and MoreHorizontal still resolve and you do not have to work out which ones moved.

Ten packages went out together: React, React Native, Vue, Preact, Solid, Svelte, Angular, a plain DOM build, the geometry on its own as JSON, and the SVG files plus a sprite for anything that is not JavaScript. One icon costs between 250 bytes and 1.18 kB depending on the target, minified and brotlied. Nothing carries a runtime dependency except Angular, which pulls tslib like every Angular library does.

Svelte and Angular get a single component that takes the icon as data. Both of them compile a component per file in your build, so shipping 1,756 files would mean 1,756 compilations in a project that imports three of them.

Where it is now

I use it in my own projects and that is the entire track record. It sits at 0.5.0, MIT.

If you do try it, the feedback worth having is whether the wobble is too much or not enough, and whether it survives at 15 pixels inside a real button. That size is where a bowed stroke either holds or turns to mud, and it is the one I keep going back to. Also worth telling me: which target is missing.

Geometry derived from Lucide 1.27.0, ISC, copyright Lucide Icons and Contributors. Not affiliated with Lucide, not endorsed by them.