All work
Live/2026

Sticky

A digital corkboard built for the people who still use paper ones.

A zero-dependency, zero-backend sticky-note board — draggable notes on a pan/zoom canvas, red-string connections, checklists and reminders, voice dictation and read-aloud, and optional end-to-end-encrypted sync — designed around the accessibility needs of older and low-vision users.

Zero
Dependencies
Offline-first
Backend
E2E encrypted
Sync
$0
Cost to run

Try it

A working miniature of the real board, running right here — the same drag model, the same red-string connections solved live as notes move, the same edit-in-place notes. Open the full app for pan/zoom, voice dictation, read-aloud, reminders, boards, and encrypted sync.

4 notes · 2 strings
  • Milk
  • Bread
  • Eggs
  • Coffee
1/4 done
📌

Drag notes · edit the text in place · 🔗 to run a string between two notes (tap 🔗 again on a connected pair to disconnect) · click a string to cut it. Deleting a note prunes its strings, exactly as the production app does.

The problem

Note apps optimize for people who already live in software: dense toolbars, account walls, sync conflicts, and text sized for 25-year-old eyes. The people who most rely on notes — someone who keeps a fridge covered in paper stickies, or a family member losing their vision — are exactly the people those apps lose.

Sticky is a deliberate constraint exercise: keep the physical metaphor people already understand (a board, a note you move with your hand, red string between related things), and make the software disappear. No login, no backend, no build step, no dependencies — open the page and your notes are there.

What I built

  • Freeform pan/zoom canvas — notes live on a transformed world surface with pinch-zoom, one- and two-finger pan, wheel pan / ⌘-wheel zoom, and a fit-all control that frames every note on the board.
  • Corkboard 'string' connections: notes are linked with SVG quadratic paths that sag under gravity, drawn beneath the notes so the yarn looks pinned underneath, and re-solved live from DOM geometry during drag and resize.
  • Accessibility as the product, not a checkbox — Web Speech dictation and read-aloud (no API keys, no cloud), a 0.8×–1.6× font scale, big tap targets, word-plus-icon labels, high-contrast light/dark themes, and a print stylesheet for people who want the board on paper.
  • Undo/redo over a checkpointed snapshot history, multi-board organization, reminders via the native OS date picker, checklists with progress, a tally/calculator note type, search, and JSON backup/restore.
  • Optional two-device sync that stays private by construction: the board is encrypted in the browser with a PBKDF2-derived AES-GCM key before it is uploaded, so the server (a Cloudflare Function + KV) only ever holds ciphertext it cannot read.
  • Hand-tuned mobile input: touch-action is scoped so a finger on a note's scrollable body still drags the note, and drags are driven by document-level pointer listeners rather than pointer capture — the fix for touch drags dying after the first move.
  • Ships as static files to Cloudflare Pages — no framework, no bundler, no npm install, and $0/month to run at any traffic level.

Architecture

  1. 1index.html + vanilla JS modules — no framework, no build step
  2. 2store.js — normalized state, checkpointed undo/redo, JSON import/export
  3. 3localStorage as the source of truth (offline-first, instant load)
  4. 4app.js — render + pointer-event drag/resize, pan/zoom camera, SVG string layer
  5. 5Web Speech API — dictation (STT) + read-aloud (TTS), keyless and on-device
  6. 6Optional sync: AES-GCM encrypt in-browser → Cloudflare Function → KV (ciphertext only)
  7. 7Static deploy → Cloudflare Pages (global CDN, $0)

What it demonstrates

Front-end engineering without a framework to hide behind: state modeling and undo, a transformed coordinate space with correct hit-testing at any zoom, real pointer-event input handling across mouse and touch, and rendering geometry (the sagging strings) that stays correct while things move.

Underneath the paper metaphor it is a system-design exercise, just a small one: where state lives and who owns it (the DOM owns the text, the store owns everything else), what the undo model has to snapshot, which coordinate space each piece of geometry is solved in, and — once sync entered the picture — where the trust boundary sits. Those decisions came first; the features fell out of them.

It also demonstrates how I choose constraints. Zero dependencies and localStorage-first weren't minimalism for its own sake — they're what makes the app load instantly, work offline, survive with no maintenance, and cost nothing to run. When sync was added, the same instinct produced an architecture where the server is structurally incapable of reading a user's notes.

Most of all it's a product built for a specific person rather than a generic user. Every feature that looks unusual — read-on-tap, the 1.6× font scale, the native calendar for reminders, the printable board — came from watching someone who is not a software person try to use it.

Engineering notes

Two bugs from this build were worth more than the features. The first: touch drags died after a single move, because touch-action is decided by whichever element the finger lands on — the note itself was set correctly, but its scrollable body child wasn't, so the browser reclaimed the gesture as a scroll. The second: pointer capture silently drops touch moves, so drags are driven from document-level listeners attached at drag-start instead.

The board is verified end-to-end with Playwright across desktop and mobile viewports — seeding, drag, connect, disconnect, delete with dangling-link pruning, theme, font scale, and reload persistence — with synthetic touch and pinch gestures dispatched through the CDP input domain.

Stack

Vanilla JavaScriptCSS (no framework)Pointer EventsSVGWeb Speech APIWeb Crypto (AES-GCM)localStorageCloudflare Pages / Functions / KV