Recipe / Collaboration

Operational Transform Primer

Operational transform (OT) is the algorithm behind real-time collaborative editors like Google Docs. It lets multiple clients mutate a shared document concurrently while converging to a single consistent state. This recipe walks through the three pieces every OT system needs and shows a tiny working transform for two concurrent insert operations against a string buffer.

1. Model operations as data

Every change a client makes becomes an explicit operation object:insert,delete, orretain. The op carries position, payload, and the client id that produced it. Never mutate the document directly; always go through an op so history is replayable.

2. Transform against concurrent ops

When two clients edit the same revision, the server receives op A and op B with the same base. It rebases B onto A (and vice versa) so that applying them in any order yields the same document. Ties on the same position are broken deterministically by client id, which guarantees convergence without locks.

3. Broadcast and acknowledge

The server stamps each transformed op with a monotonic revision number and rebroadcasts it. Each client applies remote ops to its confirmed state, then rebases its own pending buffer on top. An ack from the server promotes a pending op into confirmed history.

Reference transform

// Minimal OT transform for two concurrent inserts
function transform(opA, opB) {
  if (opA.type === 'insert' && opB.type === 'insert') {
    if (opA.pos < opB.pos) return opA;
    if (opA.pos > opB.pos) return { ...opA, pos: opA.pos + opB.text.length };
    return opA.clientId < opB.clientId
      ? opA
      : { ...opA, pos: opA.pos + opB.text.length };
  }
  return opA;
}

// Apply remote op against local state
function apply(doc, op) {
  return doc.slice(0, op.pos) + op.text + doc.slice(op.pos);
}