
Distributed sync,
beautifully composed.
The typed CRDT framework. Collaborative documents that look like plain objects, migrate themselves and live in PostgreSQL.

Why Pulsify
Local-first apps, without the hard parts
Write to an object. Pulsify renders it instantly, keeps it on the device, syncs it when it can and never lets a bad write in.
A document is a plain object
Assign a field, push to an array. The UI re-renders instantly, the change is persisted and synced in the background. No stores, no reducers, no fetch-then-mutate.
const note = await loadPulse(Note, "note-42");
subscribePulse(note, render);
note.title = "Shipped"; // rendered now
note.tags.push("launch"); // synced later
Offline is the default, not a feature
Writes never wait for the network. A tab that lost its connection keeps working, and when it comes back every edit merges without a conflict handler.
disconnectPulse(note); // airplane mode
note.body.insert(0, "Draft: ");
await connectPulse(note).synced;
// merged with everything your team did meanwhile
A CRDT that cannot be corrupted
One schema gives you the TypeScript type, runtime validation of every local and remote write, and the wire format. A bad payload from a peer is rejected, not merged.
note.tags.push(42);
// Type 'number' is not assignable to type 'string'
Everything around the CRDT, shipped
The parts you would otherwise glue together yourself come in the box, and all speak the same protocol.
- Token auth
- Lazy migrations
- Sub-documents
- PostgreSQL · SQLite
- Rich text editors
- Hosted cloud
Three steps
Define. Edit. Sync.
No operation objects, no transactions to open, no conflict handlers. Write normal TypeScript and let the engine record it.
Define
A codec schema is the single source of truth for types, validation and bytes.
import { array, definePulse, object, richText } from "pulsify-core";
export const Note = definePulse("Note", object({
title: "string",
body: richText(),
tags: array("string"),
})).materialize();
Edit
A pulse is a plain object. Assign, push, splice. Every change becomes a patch.
const note = createPulse(Note, {
title: "Draft", body: "Hello", tags: [],
});
note.title = "Ready";
note.tags.push("review");
note.body.insert(5, " world");
Sync
Add a connector. Offline edits merge on reconnection, no conflict code.
const connector = createWebSocketConnector(
"wss://sync.example.com",
);
const note = await loadPulse(Note, "note-42", {
connectors: [connector],
});
note.title = "Synced everywhere";
Benchmarks
Faster than Yjs. Much faster than Loro.
Same document, same edits, same machine. Pulsify's native engine skips binary encoding on local writes and compiles the schema into straight-line code. Lower is better.
Create and encode
1,000-item document, first export
Initial load
Import 1,000 items and materialize a JSON view
Delta round trip
Edit, encode delta, import on a peer, read
Single mutation
One field write after 5,000 edits of history
Encoded size
Bytes for a 1,000-item document
How we measured
Apple M4, Bun 1.4.4, Yjs 13.6.33 (V2 encoding), Loro 1.17.1 (WASM). Medians of 25 samples after warm-up, shuffled order, forced GC outside timing. Yjs still wins the two-peer offline merge scenario. Explore every number, json-joy, payload sizes and database results on the benchmarks page.
Full results and methodCompared
What you get out of the box
| Pulsify | Yjs | Loro | |
|---|---|---|---|
| Schema with inferred types and runtime validation | |||
| Lazy, per-document migrations | |||
| Materialized public type over stored fields | |||
| Official PostgreSQL and SQLite storage | Community | ||
| Sync queries without loading the document | |||
| Array move with preserved identity | |||
| Movable tree | |||
| Rich text with ProseMirror and Lexical bindings | ProseMirror |
One protocol
From the browser to PostgreSQL
A versioned binary envelope carries every change. The same frames go over WebSocket, in memory between tests, into a PostgreSQL row and through the Rust implementation of the protocol. Pick a local connector for instant rendering and a remote one for durability.
- Browsers
- Bun
- Node 22+
- Deno 2
- Cloudflare Workers
