- Published on
Real-time Collaboration Engines & Sync 2026 Deep Dive - Liveblocks, PartyKit, Yjs, Automerge, ElectricSQL, Replicache, Zero (Rocicorp), Convex, Triplit, Jazz.tools, Loro
- Authors

- Name
- Youngju Kim
- @fjvbn20031
"In ten years, every piece of software will be multiplayer. Single-user apps will be fossils." — Martin Kleppmann, "Local-first software" (Ink & Switch, 2019)
Watching a colleague's cursor drift across a Figma board, seeing words appear simultaneously on a Notion page, observing Linear issues refresh without a page reload — these were premium SaaS differentiators in 2020. By 2026, this multiplayer UX has become the baseline expectation of every new B2B SaaS product, and "if it doesn't collaborate like Figma, it doesn't sell" is no longer a joke.
Behind this shift is a thirty-year-old computer-science theory — CRDTs (Conflict-free Replicated Data Types) — finally working reliably in production, and a generation of new platforms (Liveblocks, PartyKit, Convex) wrapping that theory into developer-friendly SaaS. At the same time, Zero — released by Replicache founder Aaron Boodman in late 2024 — proposed a new paradigm: leave Postgres untouched, let the client synchronize via IndexedDB. ElectricSQL, PowerSync, Triplit, and Jazz.tools represent a similarly explosive growth of the local-first camp.
This article surveys the complete real-time collaboration and sync ecosystem as of May 2026. We cover CRDT cores (Yjs, Automerge, Loro), hosted SaaS (Liveblocks, PartyKit, Ably), local-first sync engines (Replicache, Zero, ElectricSQL, Convex), production case studies (tldraw, Excalidraw, Notion, Linear), and concrete Korean and Japanese examples (Toss, Naver Whale, Cybozu kintone, Sansan).
1. The 2026 Real-time Collaboration Map — Five Layers
The clearest way to understand the real-time collaboration stack is to break it into five layers.
| Layer | Role | Representative Products |
|---|---|---|
| Transport | Bidirectional browser-to-server messaging | WebSocket, WebTransport, WebRTC DataChannel, SSE |
| CRDT / OT Core | Conflict-free data structures | Yjs, Automerge, Loro, Diamond Types, ShareDB |
| Sync Engine | Client-server state synchronization | Replicache, Zero, ElectricSQL, PowerSync, Convex, Triplit, Jazz.tools |
| Hosted Realtime SaaS | Multiplayer infrastructure hosting | Liveblocks, PartyKit, Ably, Pusher, Supabase Realtime |
| Application Pattern | Cursors, comments, notifications | Figma, Notion, Linear, tldraw case studies |
Layers are independently selectable, and individual products often bundle multiple layers. Liveblocks bundles Transport + CRDT (built on Yjs) + Hosted SaaS + Application Pattern. Yjs offers only the CRDT core. PartyKit offers Transport + Hosted SaaS. ElectricSQL offers only the Sync Engine.
The first branching question when choosing is: "How much of this am I willing to run myself?" If you want SaaS-everything, pick Liveblocks. For partial self-hosting, Yjs plus a custom server. For data sovereignty, Yjs plus the full y-websocket stack on your own infrastructure. For full local-first, Automerge plus Automerge-repo.
2. CRDT Foundations — Yjs, Automerge, Loro, Diamond Types
CRDTs have been a research topic in distributed systems since the 1980s. The core promise: even when many nodes edit simultaneously, the state will eventually converge to the same value, with a mathematical guarantee. Two main families exist.
- State-based CRDT (CvRDT) — Nodes exchange full state and merge. Simple but bandwidth-heavy.
- Operation-based CRDT (CmRDT) — Nodes propagate individual operations (insert, delete). Efficient but requires reliable message delivery.
Yjs (Kevin Jahns, 2015 onward) is the most widely deployed CRDT library. Built on the YATA (Yet Another Transformation Approach) algorithm, it combines operation-based CRDTs with an efficient binary encoding (y-protocols) to handle large documents quickly. tldraw, Excalidraw, JupyterLab, Hocuspocus, and Liveblocks all build on Yjs.
import * as Y from 'yjs'
const doc = new Y.Doc()
// Y.Text — collaborative text type
const yText = doc.getText('content')
yText.insert(0, 'Hello ')
yText.insert(6, 'world!')
console.log(yText.toString()) // "Hello world!"
// Y.Map / Y.Array — collaborative object/list
const yMap = doc.getMap('user')
yMap.set('name', 'Alice')
yMap.set('cursor', { x: 10, y: 20 })
// State sync — binary update vector
const update = Y.encodeStateAsUpdate(doc)
const doc2 = new Y.Doc()
Y.applyUpdate(doc2, update)
console.log(doc2.getText('content').toString()) // "Hello world!"
// Observation — subscribe to change events
yText.observe((event) => {
console.log('Text changed:', event.changes.delta)
})
Automerge (Martin Kleppmann + Ink & Switch, 2017 onward) began as a "JSON CRDT". Automerge 2 (released in 2022) was rewritten with a Rust core and WebAssembly bindings, achieving roughly 100x performance gains. Automerge-repo (2024) bundles the sync protocol, storage adapters, and network adapters into a full-stack library.
import * as Automerge from '@automerge/automerge'
import { Repo } from '@automerge/automerge-repo'
import { BrowserWebSocketClientAdapter } from '@automerge/automerge-repo-network-websocket'
import { IndexedDBStorageAdapter } from '@automerge/automerge-repo-storage-indexeddb'
const repo = new Repo({
network: [new BrowserWebSocketClientAdapter('wss://sync.example.com')],
storage: new IndexedDBStorageAdapter('my-app'),
})
interface Doc {
title: string
items: { id: string; text: string; done: boolean }[]
}
const handle = repo.create<Doc>({ title: 'My Tasks', items: [] })
handle.change((doc) => {
doc.items.push({ id: 'a1', text: 'Buy milk', done: false })
})
// Changes from other clients are merged automatically
handle.on('change', ({ doc }) => {
console.log('Updated:', doc)
})
Loro (2023 onward, written in Rust) ships a tighter wire format than Automerge and treats Time Travel as a first-class feature. The complete edit history is preserved efficiently, making "revert to ten minutes ago" close to O(log n) rather than O(edit-count).
Diamond Types (Seph Gentle, 2022 onward) is a text-only CRDT designed to push "less than 1x overhead vs. raw text" as its target. Benchmarks favor it over Yjs in JS, but Yjs's massive ecosystem keeps adoption lower.
Y-Sweet (Drifting in Space, 2023 onward) hosts Yjs as a service, with a Rust-based open-source server (y-sweet) also available for self-hosting. Less a Liveblocks competitor than a target for teams that want Yjs but don't want to run servers themselves.
3. CRDT vs OT vs Last-Write-Wins — Conflict Resolution Strategies
Collaborative data structures resolve conflicts using one of four broad strategies.
| Strategy | Mechanism | Used By |
|---|---|---|
| Operational Transform (OT) | Transform operations against concurrent ops | Google Docs, ShareDB, Etherpad |
| CRDT | Mathematically commutative operations | Figma, Notion (partial), Linear, tldraw, Excalidraw |
| Last-Write-Wins (LWW) | Latest timestamp wins | DynamoDB, Cassandra, simple KV |
| Application-level Merge | Domain logic resolves conflicts | Git, Jira, most business apps |
Operational Transform (OT) was first formalized at Sun Microsystems in 1989, then adopted by Google Docs (2006) and Microsoft Office Live. The core idea: "If user A inserts 'x' at index 5 while user B concurrently inserts 'y' at index 3, then when B's op arrives, transform A's index 5 to 6." Intuitive but requires a central server (authority), and the transform matrix explodes beyond around 100 concurrent editors.
CRDT designs operations so that "every pair is commutative", letting them merge in any order to produce identical results without transforms. Strong on distributed and offline-first environments, weak on metadata overhead (each op needs a UUID plus a lamport clock) and on the Wikipedia-style requirement that deleted content remains as tombstones forever. Yjs, Automerge, and Loro focus on shrinking that overhead.
LWW (Last-Write-Wins) is the simplest strategy. The later timestamp wins. Great for domains with low conflict probability (e.g., a user profile's "last updated at"), but lossy for concurrent text edits.
Application-level Merge is what Git uses. On conflict, a human resolves. Jira, Asana, and most business apps follow this model and use domain rules (e.g., "the later comment goes on top") for any structural conflicts.
Selection rule of thumb: concurrent text editing wants CRDT or OT, task and issue trackers want application-level merging, simple sync wants LWW. Figma uses CRDTs, Google Docs uses OT, Linear blends LWW with domain merging.
4. Liveblocks — Leader of the Multiplayer SaaS Category
Liveblocks (liveblocks.io) was founded in France in 2021 and raised a Series A in 2024. In 2026, it is unambiguously the leader of the multiplayer SaaS category. Founded by ex-Notion engineers, its positioning is "what Notion built for itself, as a SaaS".
The core value prop is frictionless React + Yjs integration. Using Yjs directly requires running a y-websocket server, implementing awareness, and tracking presence — Liveblocks bundles all of that into a few SDK calls.
// React multiplayer component — Liveblocks
import { RoomProvider, useOthers, useUpdateMyPresence } from '@/liveblocks.config'
import { LiveblocksProvider } from '@liveblocks/react'
function App() {
return (
<LiveblocksProvider authEndpoint="/api/liveblocks-auth">
<RoomProvider id="my-room" initialPresence={{ cursor: null }}>
<CursorTracker />
<OnlineUsers />
</RoomProvider>
</LiveblocksProvider>
)
}
function CursorTracker() {
const updateMyPresence = useUpdateMyPresence()
return (
<div
onPointerMove={(e) => updateMyPresence({ cursor: { x: e.clientX, y: e.clientY } })}
onPointerLeave={() => updateMyPresence({ cursor: null })}
style={{ width: '100vw', height: '100vh' }}
/>
)
}
function OnlineUsers() {
const others = useOthers()
return (
<div>
{others.length} people are viewing this
{others.map(({ connectionId, presence }) => (
presence.cursor && (
<Cursor
key={connectionId}
x={presence.cursor.x}
y={presence.cursor.y}
/>
)
))}
</div>
)
}
Liveblocks added Comments (threaded comments) in 2024, Notifications (in-app plus email) in 2025, and Liveblocks AI Copilots (AI editors that join the room like collaborators) in late 2025. Pricing is per Monthly Active User: free up to 100 MAU, paid above.
The open-source alternative is Hocuspocus (tiptap.dev/hocuspocus), a self-hostable Node.js server for Yjs. Built by the TipTap editor team, it offers persistence, auth, and webhook hooks for free.
5. PartyKit (Acquired by Cloudflare) — Built on Durable Objects
PartyKit (partykit.io) was founded in 2023 by Sunil Pai (ex-React core team) and acquired by Cloudflare in April 2024. In 2026, PartyKit is "programmable multiplayer infrastructure" built on Cloudflare Workers + Durable Objects.
If Liveblocks is "ready-made building blocks", PartyKit is "a programmable canvas". Each "party" is one Durable Object inside which users can run arbitrary TypeScript. Chatbots, whiteboards, game rooms — any stateful realtime workload can live in one substrate.
// PartyKit server — single file
import type * as Party from 'partykit/server'
export default class ChatServer implements Party.Server {
constructor(readonly room: Party.Room) {}
// When a client connects
async onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {
// Load last 100 messages (Durable Object built-in storage)
const messages = (await this.room.storage.get<string[]>('messages')) ?? []
conn.send(JSON.stringify({ type: 'history', messages }))
}
// Receive message
async onMessage(message: string, sender: Party.Connection) {
const messages = (await this.room.storage.get<string[]>('messages')) ?? []
messages.push(message)
if (messages.length > 100) messages.shift()
await this.room.storage.put('messages', messages)
// Broadcast to all connections in the room, except the sender
this.room.broadcast(message, [sender.id])
}
}
// Client
import PartySocket from 'partysocket'
const socket = new PartySocket({
host: 'my-app.username.partykit.dev',
room: 'lobby',
})
socket.addEventListener('message', (e) => console.log(e.data))
socket.send('Hello world!')
Post-acquisition, PartyKit runs across Cloudflare's 300+ global data centers, and the "Durable Object at the edge" differentiator is sharper than ever. From 2025, PartyKit code is compatible with Cloudflare Workers, so teams can manage everything from the Cloudflare dashboard without a separate PartyKit account.
PartyKit's strength is flexibility; its weakness is the lack of high-level building blocks. Comments and Notifications, which Liveblocks offers out of the box, must be implemented yourself.
6. The Yjs Ecosystem — y-websocket, y-protocols, awareness
Yjs is not just a library; it has a thriving ecosystem of transport adapters, persistence adapters, and the awareness protocol.
y-websocket is the canonical transport adapter. A Node.js server (y-websocket-server) pairs with a browser client (y-websocket) to ship Yjs updates efficiently over WebSocket.
// Client
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'
const ydoc = new Y.Doc()
const provider = new WebsocketProvider('wss://demos.yjs.dev', 'my-room', ydoc)
// Awareness — cursors, selections, user colors
provider.awareness.setLocalStateField('user', {
name: 'Alice',
color: '#ff0000',
cursor: { x: 10, y: 20 },
})
provider.awareness.on('change', (changes) => {
for (const [clientId, state] of provider.awareness.getStates()) {
console.log(clientId, state.user)
}
})
y-indexeddb is the browser-local storage adapter. Offline edits land in IndexedDB and sync up when the network returns.
y-protocols defines three wire protocols: sync (state sync), awareness (presence), and auth.
y-monaco / y-prosemirror / y-codemirror.next and many other editor bindings exist. tldraw cites Yjs's editor integration ecosystem as a key reason for adoption — unmatched by other CRDTs.
Hocuspocus (2022 onward) is a full-stack server for Yjs by the TipTap team, with persistence, webhooks, auth, and scaling out of the box. Self-hostable with Postgres or Redis backends, plus a Hocuspocus Cloud offering as of 2025.
7. Replicache — The Original Client-first Sync Engine
Replicache (replicache.dev) was released in 2020 by Rocicorp (Aaron Boodman, ex-Roam, ex-Google) as a client-first sync engine. Adopted by Linear, Loom, Sentry, Productboard, and Coalesce.
The Replicache model:
- The client keeps its own copy of data in IndexedDB.
- User actions are defined as mutations (JS functions) and applied locally immediately.
- Mutations also travel to identically named handlers on the backend.
- The backend updates the global source of truth and returns a new pull response to the client.
- When the client applies the pull response, local optimistic mutations are discarded and replaced by server state.
// Replicache client setup
import { Replicache } from 'replicache'
type M = typeof mutators
const mutators = {
async createTask(tx, args: { id: string; text: string }) {
await tx.set(`task/${args.id}`, { text: args.text, done: false })
},
async toggleTask(tx, args: { id: string }) {
const t = await tx.get<{ text: string; done: boolean }>(`task/${args.id}`)
if (t) await tx.set(`task/${args.id}`, { ...t, done: !t.done })
},
}
const rep = new Replicache<M>({
licenseKey: process.env.NEXT_PUBLIC_REPLICACHE_LICENSE_KEY!,
name: 'user-alice',
pushURL: '/api/replicache/push',
pullURL: '/api/replicache/pull',
mutators,
})
// Called from UI — applied locally instantly, synced in the background
await rep.mutate.createTask({ id: 'a1', text: 'Buy milk' })
// Reactive query
const tasks = rep.subscribe(
async (tx) => {
const entries = await tx.scan({ prefix: 'task/' }).entries().toArray()
return entries.map(([k, v]) => ({ key: k, ...v }))
},
(tasks) => render(tasks)
)
Replicache does not impose a data model. Any data structure can sit on top of a KV store, and mutations are arbitrary JS, so any domain can be expressed. The cost is heavy server-side implementation: you write the push and pull endpoints, and reimplement mutations in SQL.
Pricing is MAU-based: free up to 1,000 MAU, $1 per MAU above. From 2024, Rocicorp shifted focus to its successor, Zero, and recommends Zero for new adoption.
8. Zero (Rocicorp, 2024) — The Next-Generation Sync Engine
Zero (zero.rocicorp.dev) was announced by Replicache founder Aaron Boodman in November 2024. The core idea: replace Replicache's KV model with first-class relational queries.
The Zero model:
- The backend is plain Postgres.
- The client keeps a local copy in SQLite (or a WASM-SQLite-on-IndexedDB).
- Users write queries like
z.query.issues.where('status', 'open').orderBy('updatedAt', 'desc').limit(10). - Zero syncs only data that could affect the query (a rolling subscription).
- Queries execute against IndexedDB for zero-latency responses.
- Server-side changes stream over WebSocket and re-evaluate queries automatically.
// Zero in practice — Linear-style issue tracker
import { Zero } from '@rocicorp/zero'
import { useQuery } from '@rocicorp/zero/react'
const z = new Zero({
userID: currentUser.id,
server: 'wss://zero.example.com',
schema: mySchema, // Can be auto-extracted from Postgres
kvStore: 'idb',
})
function IssueList() {
const [issues] = useQuery(
z.query.issues
.where('status', '=', 'open')
.related('assignee')
.related('comments', (q) => q.orderBy('createdAt', 'desc'))
.orderBy('priority', 'desc')
.limit(50)
)
return (
<ul>
{issues.map((issue) => (
<li key={issue.id}>
{issue.title} - {issue.assignee?.name}
<span>{issue.comments.length} comments</span>
</li>
))}
</ul>
)
}
// Mutations are automatically optimistic — UI updates in 0ms and syncs in the background
await z.mutate.issues.update({ id: 'iss_1', status: 'closed' })
The biggest shift Zero brings over Replicache is in the read model. Replicache forced manual KV scans; Zero offers an ORM-style query builder with SQL-like expressiveness. JOINs and nested relations are first-class, so complex domains like Linear feel much more natural.
Zero hit stable in July 2025, and in 2026 Linear is mid-migration from Replicache to Zero (see Linear's December 2025 engineering blog post).
9. ElectricSQL — Bidirectional Postgres + SQLite Sync
ElectricSQL (electric-sql.com) was founded in the UK in 2022 with the clear mission of "bidirectionally syncing Postgres with client-side SQLite". In late 2024 the team pivoted significantly: they replaced the original CRDT-based approach with a Phoenix LiveView-style server-authority plus shape-based subscription model.
The new ElectricSQL model:
- Backend: Postgres plus the Electric sync service (written in Elixir).
- Client: PGlite (WASM Postgres for browsers) or SQLite as the local copy.
- The client subscribes to shapes (e.g., "active employees of my company").
- Postgres logical replication feeds only relevant changes to subscribed clients.
- Writes go via a plain HTTP API; once the Postgres transaction commits, changes propagate automatically.
import { Electric, electrify } from 'electric-sql/browser'
import { schema } from './generated/client'
// PGlite or wa-sqlite as the local client DB
const electric = await electrify(database, schema, {
url: 'wss://electric.example.com',
})
// Subscribe to a shape — active employees in my company
const shape = await electric.sync.subscribe({
table: 'employees',
where: 'company_id = $1 AND status = $2',
bindings: [currentUser.companyId, 'active'],
include: { manager: true }, // Auto-follow FK
})
await shape.synced
// Query the local copy with normal SQL — instant against IndexedDB
const employees = await electric.db.employees.findMany({
where: { department: 'Engineering' },
})
// Writes go via HTTP POST — committed transactionally on the backend Postgres
await fetch('/api/employees', {
method: 'POST',
body: JSON.stringify({ name: 'Alice', department: 'Engineering' }),
})
// Changes propagate to all subscribers automatically
ElectricSQL's strength is that it leaves your Postgres alone. Existing Rails, Django, or Next.js backends keep working with minimal change, and ORMs continue to function. The weakness: under the new server-authority model, clients cannot write directly. If you want optimistic updates, you must layer something else (e.g., TanStack Query's useMutation with onMutate).
From 2025 the team has deepened integrations with Postgres hosts (Vercel, Supabase, Neon), so the "Vercel Postgres plus ElectricSQL" stack can build a local-first app without extra infra.
10. PowerSync — Mobile-first Sync
PowerSync (powersync.com) launched in 2023 as a sync engine targeting mobile apps (iOS/Android/React Native/Flutter) as a first-class use case. It supports Postgres, Supabase, and Firebase backends.
The PowerSync model resembles ElectricSQL's, but adds first-class mobile SQLite and bucket-based partitioning so each user only syncs the data they need.
// React Native + PowerSync
import { PowerSyncDatabase } from '@powersync/react-native'
import { SupabaseConnector } from './supabase-connector'
const db = new PowerSyncDatabase({
schema: AppSchema,
database: { dbFilename: 'app.db' },
})
await db.connect(new SupabaseConnector())
// Reads — always against local SQLite, instant
const issues = await db.getAll(
'SELECT * FROM issues WHERE assignee = ? ORDER BY priority DESC',
[currentUser.id]
)
// Writes — applied locally instantly, pushed to Supabase in the background
await db.execute(
'UPDATE issues SET status = ? WHERE id = ?',
['closed', 'iss_1']
)
PowerSync excels at offline-first scenarios: subways, airplanes, mountain valleys. Apps run instantly offline and sync back on reconnect, making it popular with field service, healthcare, and agriculture B2B mobile apps.
From 2025, PowerSync also supports the web (React/Next.js), competing directly with ElectricSQL.
11. Convex — A Reactive Full-stack Backend
Convex (convex.dev) was founded in 2021 and raised a Series B in 2024. It pitches "database plus functions plus sync, bundled" — often compared to a next-generation Firebase.
The hallmark of Convex is that every query is automatically reactive. A client calling useQuery(api.tasks.list) is automatically re-run whenever a mutation that could affect the query is committed.
// Convex — server function
// convex/tasks.ts
import { query, mutation } from './_generated/server'
import { v } from 'convex/values'
export const list = query({
args: { ownerId: v.id('users') },
handler: async (ctx, args) => {
return await ctx.db
.query('tasks')
.withIndex('by_owner', (q) => q.eq('ownerId', args.ownerId))
.order('desc')
.collect()
},
})
export const create = mutation({
args: { text: v.string() },
handler: async (ctx, args) => {
const userId = (await ctx.auth.getUserIdentity())!.subject as Id<'users'>
return await ctx.db.insert('tasks', {
text: args.text,
ownerId: userId,
done: false,
})
},
})
// Convex — React client
import { useQuery, useMutation } from 'convex/react'
import { api } from '../convex/_generated/api'
function TaskList() {
const tasks = useQuery(api.tasks.list, { ownerId: currentUser._id })
const createTask = useMutation(api.tasks.create)
// Re-renders automatically when another user adds a task
return (
<div>
<button onClick={() => createTask({ text: 'New task' })}>Add</button>
{tasks?.map((t) => <div key={t._id}>{t.text}</div>)}
</div>
)
}
Convex's strength is magical DX. You never touch WebSocket, subscription, or invalidation logic; every query reacts automatically. The database, functions, file storage (ctx.storage), scheduler (ctx.scheduler), and external actions (ctx.runAction) all share one SDK.
The weakness is vendor lock-in and the lack of on-prem options (as of May 2026), though a self-hosted Convex beta was released for select enterprises in 2025.
12. Triplit — TypeScript-first Full-stack Sync
Triplit (triplit.dev) was founded in 2023 with the goal of "end-to-end TypeScript type safety with local-first sync". It is smaller and simpler than Convex, and open source (triplit-io/triplit).
import { TriplitClient, Schema as S } from '@triplit/client'
const schema = {
collections: {
todos: {
schema: S.Schema({
id: S.Id(),
text: S.String(),
completed: S.Boolean({ default: false }),
createdAt: S.Date({ default: S.Default.now() }),
}),
},
},
}
const client = new TriplitClient({
serverUrl: 'https://triplit.example.com',
schema,
})
// Query — results instantly local; server changes auto-pushed
const subscription = client.subscribe(
client.query('todos').where('completed', '=', false).order('createdAt', 'DESC'),
(results) => render(results)
)
// Transaction — optimistic with auto conflict resolution
await client.transact(async (tx) => {
await tx.insert('todos', { text: 'Buy milk' })
})
Triplit's strengths are schema-first design and full-stack type inference. There are no separate server functions; permission rules (read/write/delete) are declared on the schema.
13. Jazz.tools — Distributed State Framework
Jazz.tools (jazz.tools), founded in 2023, calls itself a "distributed state framework". Rather than a database, it shares CRDT objects between clients via P2P or cloud sync.
import { co, CoMap, CoList } from 'jazz-tools'
class Task extends CoMap {
text = co.string
done = co.boolean
}
class TaskList extends CoList.Of(co.ref(Task)) {}
// Create a new task list (CRDT object)
const list = TaskList.create([], { owner: me })
// Share with other users (by URL)
const shareUrl = list.id
console.log(`Share: https://app.example.com/list/${shareUrl}`)
// Mutation — propagated to all subscribers automatically
list.push(Task.create({ text: 'Buy milk', done: false }, { owner: me }))
Jazz.tools's strength is E2E encryption + P2P options. All data is encrypted client-side; the server only sees ciphertext, providing a strong security model.
14. InstantDB, Evolu, Vlcn — Emerging Players
InstantDB (instantdb.com, 2023 onward) is a Relay/Firebase-inspired GraphQL-like client-first database. Queries are automatically reactive and transactions are optimistic. Raised a Series A in 2025.
Evolu (evolu.dev, 2024 onward) leans hard on local-first plus end-to-end encryption. SQLite lives on the client and only encrypted changes go to the server, making it a good fit for apps handling sensitive data like passwords.
Vlcn / cr-sqlite (2023 onward) is a native SQLite extension that turns SQLite into a CRDT. Plain SQL keeps working, while sync and merge run automatically. Built by Tantaman (Matt Wonlaw); partially used at Discord.
15. WebSocket vs WebRTC vs SSE vs WebTransport
Collaboration transports come in four flavors.
| Protocol | Model | Strengths | Weaknesses |
|---|---|---|---|
| WebSocket | Client-server bidirectional TCP | Universal across browsers and backends | Head-of-line blocking, partial messages |
| WebTransport | Streams over HTTP/3 | Multiplexed streams, reliability options | No Safari support until 2026, limited backends |
| WebRTC DataChannel | P2P, UDP-based | Ultra-low latency, zero server load | NAT traversal, signaling required |
| Server-Sent Events | One-way server-to-client | Pure HTTP, simple | One-way only |
WebSocket is the de facto standard. Supported in every browser, with mature libraries in Node.js, Go, Rust, and Python. Liveblocks, PartyKit, Yjs, and Convex all run on WebSocket. The limit: one message queue per connection, leading to head-of-line blocking.
WebTransport is a multiplexed transport over HTTP/3 (QUIC). Chrome, Edge, and Firefox have shipped since 2024; Safari added it in a 2026 beta. Multiple parallel streams in one connection eliminate head-of-line blocking. Backend support is still narrow (Go's quic-go, Cloudflare Workers, etc.).
WebRTC DataChannel offers P2P direct connections for ultra-low-latency messaging, with no server in the path. Drawbacks: NAT traversal (STUN/TURN servers) and a separate signaling channel. Figma's multiplayer cursors reportedly use WebRTC DataChannels for some paths.
SSE (Server-Sent Events) is the simplest one-way push over HTTP. Common for LLM streaming (ChatGPT-style) and notifications, but insufficient for bidirectional collaboration.
Choosing: general collaboration uses WebSocket, ultra-low-latency games or cursors use WebRTC, AI streaming or notifications use SSE, forward-looking projects pick WebTransport.
16. Ably, Pusher, Soketi, Centrifugo — General Realtime Pub/Sub
Not direct collaboration tools, but frequently used as backbones.
Pusher (pusher.com, 2010 onward, MessageBird acquisition) is the oldest realtime SaaS and the de facto standard for chat and notification backbones via "Pusher Channels". By 2026, direct adoption by modern SaaS has declined, but a large legacy footprint keeps it relevant.
Ably (ably.com, 2016 onward) is Pusher's successor with global distribution + 99.999% SLA + ordering guarantees as its pitch. Adopted by Hubspot, Vercel, and Splunk.
Soketi (soketi.app, 2022 onward) is an open-source server compatible with the Pusher protocol. Drop in to keep your Pusher code while self-hosting. Successor to Laravel WebSockets.
Centrifugo (centrifugal.dev, Russian-origin open source, 2016 onward) is a high-performance realtime messaging server in Go. Battle-tested by Russian giants like Avito and VK, handling a million concurrent connections per node.
Supabase Realtime (supabase.com/realtime) blends Postgres LISTEN/NOTIFY with custom broadcast channels in an open-source service. Built on Elixir/Phoenix, leveraging Erlang/OTP for distribution. The lowest-friction option if you are already on Postgres and want to add collaboration.
17. tldraw and Excalidraw — Yjs in Production Whiteboards
tldraw (tldraw.com), built by Steve Ruiz, is an infinite-canvas whiteboard that raised a Series A in 2024. Its collaboration engine was initially custom-built but migrated to Yjs in late 2023. The reasoning is laid out in the team's "How tldraw's sync server works" post (2024).
Excalidraw (excalidraw.com), created by Christopher Chedeau (Vjeux, ex-Meta), is a hand-drawn-style whiteboard with a paid Excalidraw+ tier on top of the same open-source collaboration engine. The standard Yjs + y-websocket combo; the self-hostable room server (excalidraw/excalidraw-room) is public.
Both products are concrete proof that Yjs works in production, and serve as a reference standard for FigJam, Miro, and Mural.
18. Notion, Linear, Figma — The Peaks of Collaboration UX
Notion is reportedly built on a custom OT-based collaboration engine (no official disclosure). In 2024, job postings revealed a partial migration toward local-first patterns, perhaps inspired by Anytype.
Linear adopted Replicache early on and is migrating to Zero in 2025. Linear's snappy UX — "every action runs in 0ms against local IndexedDB and syncs in the background" — is the best-known showcase of that paradigm.
Figma uses a custom CRDT, documented in Evan Wallace's 2019 essay "How Figma's multiplayer technology works". The core: per-property LWW combined with a custom CRDT for parent-child tree transformations (each child has exactly one parent).
19. Code Editor Realtime — VS Code Live Share, Replit, CodeSandbox
VS Code Live Share (Microsoft, 2017 onward) lets you join a colleague's IDE for shared editing and debugging sessions. Runs over Microsoft's own backend (Visual Studio Live Share Service) and is free.
CodeSandbox / CodeSandbox Live (codesandbox.io) started as a browser IDE and in 2024 pivoted to Firecracker-based cloud development environments. Live mode allows multi-user editing in the same browser IDE.
Replit Multiplayer combines concurrent editing with voice and video in Replit's IDE. Heavily used in education.
CodeTogether (codetogether.com) is a plug-in-based service that enables concurrent editing across IntelliJ, Eclipse, and VS Code.
20. Game State Sync — Colyseus, Photon, Hathora
Game multiplayer has different transport and consistency requirements than productivity collaboration.
Colyseus (colyseus.io) is a Node.js multiplayer game framework with a room-based model and a schema-based sync system. Popular for .io games and mobile titles.
Photon Engine (photonengine.com) is the de facto multiplayer SaaS for Unity and Unreal game development. The SDK line includes PUN (Photon Unity Networking) and Photon Fusion.
Hathora (hathora.dev) provides game-server hosting and matchmaking SaaS, removing the burden of operating game servers across global regions.
PlayCanvas Engine (playcanvas.com) is a browser-based 3D game engine with built-in multiplayer support.
Key differences vs. collaboration SaaS: tick rates (60fps means a 16.67ms cycle), client prediction plus server-authoritative reconciliation, and dead reckoning — all game-specific patterns.
21. Pattern Library — Cursors, Comments, Notifications, Activity Feeds
Most real-time collaboration UX boils down to combinations of four patterns.
1) Cursors + Presence — "Who is looking at what right now?" Liveblocks calls it Presence, Yjs calls it awareness, PartyKit calls it connection. Cursor positions, selection regions, and user avatars are the core data.
// Liveblocks Presence example
import { useUpdateMyPresence, useOthers } from '@/liveblocks.config'
function CursorLayer() {
const updateMyPresence = useUpdateMyPresence()
const others = useOthers()
return (
<div
onPointerMove={(e) => updateMyPresence({ cursor: [e.clientX, e.clientY] })}
>
{others.map(({ connectionId, presence }) => (
presence.cursor && (
<RemoteCursor key={connectionId} x={presence.cursor[0]} y={presence.cursor[1]} />
)
))}
</div>
)
}
2) Live Multiplayer Editing — Concurrent text or shape editing. The bread and butter of CRDT/OT engines.
3) Comments + Threads — Threaded comments anchored to specific regions of a page. The hard part is anchor stability: if I commented on "world" in "Hello world", and someone edits the doc to "Hello cruel world", where should the anchor go? Yjs solves this via Y.RelativePosition.
4) Notifications + Activity Feed — "Someone mentioned you", "Issue closed". Liveblocks Notifications, Knock, and Courier all target this surface.
22. Korean Case Studies — Toss, Naver Whale, Kakao Workspace
Toss Workspace — Toss's internal collaboration tool is custom-built, with Yjs-based realtime sync, as partially disclosed at the 2024 Toss SLASH conference. The notable wrinkle is financial-regulation logging: every collaboration event must be retained as audit logs for five years.
Naver Whale Collaboration / Naver Office — Naver Office (Naver Docs, Slides) uses a custom OT-based collaboration engine and runs inside the Naver Whale browser. "Whale Spaces", announced in 2025, mirrors PartyKit by providing arbitrary domain-specific realtime components.
Kakao Workspace / Kakao Agit — Kakao's internal collaboration tools are custom-built, with no detailed public architecture.
NetFunnel — STCLab's NetFunnel is more about handling massive concurrent traffic (virtual waiting rooms) than collaboration per se, but the "many users hitting the same resource at once" pattern places it adjacent to the collaboration category.
Line Note / Worktile Korea — Line's internal collaboration tools are shared with the Japan headquarters, with limited public disclosure.
23. Japanese Case Studies — Cybozu kintone, Sansan, Backlog (Nulab), JaaS
Cybozu kintone — Cybozu is Japan's largest collaboration SaaS company. kintone (2011 onward) is a no-code business app builder that also supports concurrent editing. Built on a custom OT engine; partial Yjs migration of some modules was announced in 2024.
Sansan Bill One — Sansan's Bill One invoice management SaaS built its own collaboration engine to handle multiple reviewers concurrently editing invoices. Some details were shared at the 2025 Sansan Tech Conf.
Backlog (Nulab) — Nulab, based in Fukuoka, ships Backlog, an issue tracker plus Git plus wiki, with real-time concurrent editing limited to the wiki and powered by a custom OT engine.
JaaS (Jitsi as a Service) Japan region — 8x8's JaaS offers video conferencing plus whiteboards to Japanese enterprises through a Tokyo region. The whiteboard module is built on Excalidraw (Yjs).
Notion Japan — Notion launched in Japan in 2023 with a localized UI, billing, and support. Usage at Mercari, freee, and Cyberagent has grown rapidly.
24. Operational Pitfalls — Awareness Flooding, Sync Storms, Conflict Spikes
Three operational pitfalls show up at scale.
1) Awareness flooding — Fast cursor movement triggers awareness updates every 100ms; with 50 people in a room you can see 25,000 tiny messages per second. The fix: throttling (e.g., once per 60ms) plus delta encoding (send only what changed).
2) Sync storms — A new client joining must sync the entire document, so a 500KB Yjs document times 50 simultaneous joiners equals 25MB of bandwidth. The fix: rate limiting plus CDN caching (serve the initial sync from a static snapshot).
3) Conflict spikes — Many users typing into the same cell explodes merge costs. At the UX layer, the trick is balancing a soft lock (a small lock icon when a user enters a cell) with optimistic writes.
25. Cost — Where Liveblocks and Self-hosted Yjs Cross Over
Rough cost comparison (May 2026):
- Liveblocks — Free up to 100 MAU, 499/month at 10,000 MAU, around $4,000/month at 100,000 MAU.
- PartyKit — Cloudflare Workers pricing: 1M requests/day free, 0.50/M GB-s.
- Self-hosted Yjs + y-websocket — A single t3.medium (120/month) plus Postgres scales to about 1,000 rooms.
- Hocuspocus Cloud — Roughly $99/month for 1,000 concurrent connections.
The crossover sits between 10,000 and 50,000 MAU. Below that range, Liveblocks is dramatically cheaper; above it, self-hosting wins on raw infrastructure. Of course, self-hosting carries SRE labor cost too, so the comparison is not purely about cloud spend.
26. 2026 Trends — Local-first Renaissance and the AI Copilot Wave
Three big trends are visible.
Local-first Renaissance — Linear, Notion, and Figma have all signaled "faster UX equals local-first" as their answer. The 2024-2025 explosion of Zero, ElectricSQL, Triplit, and Jazz.tools positions 2026 as the year "local-first becomes the default for new SaaS".
The AI Copilot Wave — Patterns like Liveblocks AI Copilots (2025) show AI agents joining collaboration rooms and editing documents alongside humans. Cursor, Continue, and Aide point the same way. CRDT's strength — "non-human agents can share the same data structure" — is finally paying off.
WebTransport Adoption — Safari shipping WebTransport in a 2026 beta will accelerate adoption of the "next-generation transport". PartyKit and Liveblocks have already started groundwork to migrate to WebTransport backbones.
27. References
- Yjs documentation —
https://docs.yjs.dev/ - Yjs GitHub —
https://github.com/yjs/yjs - Automerge —
https://automerge.org/ - Automerge-repo —
https://github.com/automerge/automerge-repo - Loro CRDT —
https://www.loro.dev/ - Diamond Types —
https://github.com/josephg/diamond-types - Liveblocks documentation —
https://liveblocks.io/docs - PartyKit documentation —
https://docs.partykit.io/ - Hocuspocus (TipTap) —
https://tiptap.dev/docs/hocuspocus - Replicache —
https://replicache.dev/ - Zero (Rocicorp) —
https://zero.rocicorp.dev/ - ElectricSQL —
https://electric-sql.com/ - PowerSync —
https://www.powersync.com/ - Convex —
https://docs.convex.dev/ - Triplit —
https://triplit.dev/ - Jazz.tools —
https://jazz.tools/ - InstantDB —
https://www.instantdb.com/ - Evolu —
https://www.evolu.dev/ - cr-sqlite (Vlcn) —
https://github.com/vlcn-io/cr-sqlite - Ably documentation —
https://ably.com/docs - Pusher documentation —
https://pusher.com/docs - Soketi —
https://soketi.app/ - Centrifugo —
https://centrifugal.dev/ - Supabase Realtime —
https://supabase.com/docs/guides/realtime - Colyseus —
https://docs.colyseus.io/ - Photon Engine —
https://www.photonengine.com/ - Hathora —
https://hathora.dev/docs - tldraw sync —
https://tldraw.dev/docs/sync - Excalidraw —
https://github.com/excalidraw/excalidraw - VS Code Live Share —
https://visualstudio.microsoft.com/services/live-share/ - Local-first software (Ink & Switch) —
https://www.inkandswitch.com/local-first/ - How Figma's multiplayer technology works —
https://www.figma.com/blog/how-figmas-multiplayer-technology-works/ - Standard Webhooks —
https://www.standardwebhooks.com/ - WebTransport explainer —
https://web.dev/articles/webtransport - WebRTC DataChannel —
https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel - Toss SLASH conference —
https://toss.tech/slash - Cybozu kintone developer —
https://cybozu.dev/ - Sansan Tech Blog —
https://buildersbox.corp-sansan.com/ - Backlog (Nulab) —
https://backlog.com/