penna
Draw a signature, get back an SVG that writes itself — 1 KB, no scripts. The mark in this site's header came out of it.
- To
- hiring@example.com
- Subject
- Re: the frontend role
Thanks for the notes — revised deck attached.
Best,
Haris Vreto · Senior Full-stack Engineer
harisvreto.dev
Penna is a single page. You draw your signature on a pad, it replays as a self-writing animation, and you take it away as an SVG that animates on its own — in any browser, any img tag, any CSS background. No JavaScript ships with the file.
The signature in this site's header is a Penna export. That was the test: if I wouldn't put it on my own front page, it wasn't finished.
What it makes
Every stroke becomes one path. Each path gets a stroke-dasharray equal to its own arc length and a generated @keyframes rule that walks stroke-dashoffset to zero during that stroke's slice of the timeline. All the paths share one duration, so a single inline style block staggers the whole signature. There is no script tag in the output and nothing to fetch.
The numbers, measured on the same three strokes:
- Animated SVG — 1.3 KB. Transparent, vector, animates anywhere an SVG renders.
- APNG — 14.8 KB. Transparent with real 8-bit alpha. For GitHub READMEs, which strip the
styleblock out of proxied SVGs and show them frozen. - GIF — 228 KB. No usable transparency, 175× the SVG. It exists for Slack and email clients that understand nothing else.
GIF's alpha is one bit — a pixel is either fully opaque or fully gone. A signature is antialiased round-cap strokes, which are almost entirely partial alpha along every edge, so a transparent GIF gives you a jagged halo instead of a cutout. That is why the GIF export mattes onto a solid colour and why APNG exists at all.
Timing is geometry
The thing that makes a replay read as handwriting rather than playback is that stroke duration is derived, not fixed. A stroke takes length / (620 × speed) seconds, with a 0.14s pause between strokes. Long flourishes take longer than short ticks, because in life they do.
The same path data feeds both the on-screen canvas and the exported file, so the preview and the export cannot drift apart.
Stack
Vite, React, TypeScript. The engine — geometry, timeline, SVG builder — is a dependency-free module with no React import and no DOM access beyond a 2D canvas context, so lifting it into a package is a move rather than a rewrite. Encoders are code-split and only load when you click their button. 40 tests, Lighthouse 100 across all four categories on mobile and desktop.
I wrote up how the animation actually works in Signatures that sign themselves.
Questions about this one? The terminal twin has opinions — ⌘K