Timbre

Role
Solo — design, frontend, backend, infra
Period
Jul — Aug 2026
Stack
Next.js 16, TypeScript, MongoDB, Socket.io, Yjs, Vercel Blob, Firebase Auth

The problem

Voice notes are treated as attachments — a recording bolted onto a text note, searchable by neither. The audio is the thing you actually captured, and every tool buries it.

What I did

Built a note-taking app where the recording is the note itself. Two editors share one document model: a spatial canvas and a linear document. Documents merge conflict-free over CRDTs, so two people can type in the same place and neither loses work, and they keep working offline and re-sync on reconnect.

What happened

Shipped v1.0, live at timbrenotes.xyz. Notes export as a single portable .vnote file with the audio inside it, and a share link plays back with no account at all. The OpenAPI reference generates from the route handlers themselves.

Why the canvas and the document share a model

Most editors pick one shape and make you live with it. Timbre lets a note be spatial or linear, chosen at creation, but both write into the same underlying document — so voice pins on a canvas and inline audio blocks in a document are the same object rendered two ways.

That decision is what made the .vnote export possible at all. A portable file needs one thing to serialise, not two.

Collaboration that survives a bad network

Presence and live cursors are the easy half. The hard half is what happens when two people edit the same paragraph on a train.

The Socket.io relay holds no state and owns no database. It forwards updates and nothing else, which means it can restart mid-session without anyone noticing.

What v1 deliberately leaves out

A five-minute recording budget per account, enforced server-side rather than in the client. Transcription is opt-in per recording instead of automatic — it costs money and most notes never need it.