remdom · remjs

Stream the DOM as mutation ops. Replicate the JS event loop.

Two open-source TypeScript libraries. remdom mirrors a live DOM as a structured op stream; remjs replays a JS runtime on a follower. Transport-agnostic, MIT. The platform manages long-lived remdom peers — listener servers you spin up, connect to, and tear down on demand.

What you can build

Whole-document mirroring and faithful runtime replication open a broad set of use cases. The libraries don't assume what's downstream.

The libraries

Two open-source TypeScript libraries. MIT. Use either alone, or compose them — independent of any runtime.

remdom

Stream the entire DOM as structured mutation ops.

Observe every mutation to a live document — anywhere in the tree — and encode it as a structured op stream. Not a subtree, not a virtual DOM: the real browser DOM, from <html> down. Virtualize and stream it between any source and any number of clients.

TypeScript · MIT · @remdom/dom + 4 sibling packages

remjs

Event loop replication for JavaScript.

A running JS program is a state machine. Capture the inputs that cross a leader runtime's event loop — clicks, timers, network responses, reads of Math.random and Date.now — and apply them on a follower. Same code, same state. No re-execution of business logic on the follower.

TypeScript · MIT · v0.5.7

┌──────────────────────┐                    ┌──────────────────────┐
│  leader runtime      │                    │  follower runtime    │
│                      │                    │                      │
│  patches/*  ──emit──►│        ops         │◄──apply── player.ts  │
│  recorder.ts         │═══════════════════►│                      │
│                      │       codec        │                      │
└──────────────────────┘                    └──────────────────────┘

Diagram from the remjs README, MIT.

How it works

  1. The libraries do the encoding. remdom attaches a MutationObserver and emits a structured op for every DOM change. remjs patches event-loop entry points (events, timers, fetch, Math.random, Date.now, storage) and emits the inputs a follower needs to reach the same state.
  2. You choose the transport. WebSocket, WebRTC, postMessage, in-process call, a log file. The libraries don't care.
  3. Compose them, or use either alone. Stream just the DOM; replicate just the runtime; or run both side-by-side for a full mirrored session.
  4. Run peers yourself, or let the platform. Self-host @remdom/server and your source DOMs anywhere — or use the managed runtime to spin up long-lived peers on demand.

The platform: managed remdom peers

The libraries handle the protocol. The platform manages the peers that speak it: long-lived listener servers, source-DOM hosts, and programmatic clients that devs spin up, connect to, and tear down on demand. The unit is a peer. The workflow is lifecycle-driven.

What a peer is

The lifecycle

// spin one up — stays warm for hours, days, weeks
const listener = await remdom.peers.create({
  role: "listener",
  region: "us-east",
});

// connect a source to it, then point clients at the listener
await listener.attach({ source: sourcePeerId });

// later, when you don't need it
await listener.shutdown();

What you get with the platform

Pricing TBD at GA — likely billed per peer-hour with a free tier. Libraries stay MIT regardless.

Stay in the loop

If the managed runtime is interesting, drop your email — one note when it opens, no spam.

Optional. The libraries ship today.