Package Exports
- @grayfalcon666/loom
- @grayfalcon666/loom/loom.js
- @grayfalcon666/loom/loom.wasm
- @grayfalcon666/loom/slim
Readme
@grayfalcon666/loom
A Go-powered CRDT engine for collaborative text editing, compiled to WebAssembly and running entirely in the browser — no backend required for the CRDT itself.
Features
- RGA (Replicated Growable Array) — character-level CRDT text editing with concurrent insert/delete support
- LWW-Map — key-value CRDT map
- Zero-config init — WASM is embedded as base64,
npm installand go - Full TypeScript type declarations included
- Go + syscall/js — all CRDT logic runs in the browser via WASM
Install
npm install @grayfalcon666/loomQuick Start
import { init } from '@grayfalcon666/loom';
const loom = await init(); // WASM loaded from embedded base64 — no extra files
const aliceDoc = loom.newDocument('alice');
const bobDoc = loom.newDocument('bob');
loom.addText(aliceDoc, 'body', 'shared-text');
// Alice inserts 'H' at position 0
const op = loom.insertChar(aliceDoc, 'shared-text', 0, 'H'.charCodeAt(0));
// Send op to your WebSocket server → peers receive it
// Bob applies the remote op
loom.unwrapReceive(bobDoc, op);
console.log(loom.peekText(bobDoc, 'shared-text')); // 'H'WebSocket Sync
loom does not include a WebSocket server. Any WS relay works. See the server implementation for a reference Go backend.
const WS_URL = 'ws://localhost:8080/ws?doc=my-doc';
const ws = new WebSocket(WS_URL);
// Send local op to peers
ws.addEventListener('open', () => {
const op = loom.insertChar(doc, 'text-1', 0, 'X'.charCodeAt(0));
ws.send(op);
});
// Apply remote op from peers
ws.addEventListener('message', (e) => {
loom.unwrapReceive(doc, e.data);
});API Reference
init(wasmUrl?)
Initialize the WASM module.
- No argument (default): uses the embedded base64-encoded WASM. Zero network requests, no CORS issues.
wasmUrl: load from a custom URL. Useful if you want to hostloom.wasmyourself.
// Zero-config
const loom = await init();
// Custom URL (e.g. from /slim export or CDN)
const loom = await init('https://cdn.example.com/loom.wasm');Document Management
| Method | Returns | Description |
|---|---|---|
newDocument(actorID) |
number (docID) |
Create a CRDT document |
freeDocument(docID) |
void |
Free memory |
save(docID) |
string (JSON) |
Serialize document |
load(snapshotJSON) |
number (new docID) |
Load from snapshot |
getActorID(docID) |
string |
Get actor ID |
Text Editing
| Method | Returns | Description |
|---|---|---|
addText(docID, key, textObjID) |
string (Op JSON) |
Declare shared text object |
insertChar(docID, textObjID, index, charCode) |
string (Op JSON) |
Insert character |
deleteChar(docID, textObjID, index) |
string (Op JSON) |
Delete character |
peekText(docID, textObjID) |
string |
Get current text |
insertChar/deleteCharreturn an Op JSON string — send this to your WebSocket serveraddTextalso returns an Op — call it on all peers before insertingunwrapReceive(docID, opJSON)applies a remote Op to a document
LWW-Map
| Method | Returns | Description |
|---|---|---|
set(docID, key, value) |
string (Op JSON) |
Set key-value |
get(docID, key) |
string (JSON) |
Get value |
Package Structure
@grayfalcon666/loom/
├── index.js # Main entry — init() with embedded base64 WASM
├── loom.js # Raw JS wrapper (same as index.js but older API)
├── loom.d.ts # TypeScript declarations
├── loom.wasm # Raw .wasm binary (used if you pass url to init())
├── wasm_exec.js # Go WASM bootstrap (required by Go runtime)
└── embedded_wasm.js # AUTO-GENERATED: base64-encoded loom.wasmSaving / Loading
// Save to localStorage
localStorage.setItem('doc', loom.save(docID));
// Load on page refresh
const id = loom.load(localStorage.getItem('doc'));Subpath Exports
// Full package (base64 WASM embedded — recommended)
import { init } from '@grayfalcon666/loom';
// Slim version — requires you to host loom.wasm separately
// (loom.wasm is ~3MB; slim skips the embedded base64 for smaller JS bundle)
import { init } from '@grayfalcon666/loom/slim';
const loom = await init('/node_modules/@grayfalcon666/loom/loom.wasm');TypeScript
Full TypeScript declarations are included. No @types/ package needed.
import { init, type LoomAPI } from '@grayfalcon666/loom';
const loom: LoomAPI = await init();
const docID: number = loom.newDocument('alice');Limitations
syscall/jsis the standard Go WASM path. Note that Go has deprecatedsyscall/jsand the recommended future path is TinyGo + wasm-tools.- This package is currently at v1.0.0 — API is stabilizing but breaking changes are possible in minor releases until v2.
- The WASM is large (~3MB) because it includes the full Go runtime. Consider using the
/slimexport if bundle size matters.
License
MIT