DOM ENGINEV0.21.3FIZLET

05 DOCS · RECALL

Recall gives
the page back.

Every change Fizlet makes to the page goes through one ledger. It records the original and what the engine wrote. destroy() puts back everything the engine still owns, newest first, and the DOM matches the snapshot taken before. That is Recall: the reason a game can knock a page's headings around and leave nothing behind.

THE LEDGER · scene.ts
const e = createEngine({ root: document.body });
e.add({
  render(e) {
    e.ledger.style(heading, "transform", "rotate(-4deg)"); // one inline property
    e.ledger.cls(heading, "smashed", true);                // one class
    e.ledger.attr(heading, "data-hp", "2");                // an attribute (null removes)
    e.ledger.text(word, "0101");                           // a text node's value
  },
});
const canvas = e.ledger.add(document.createElement("canvas")); // a node you add
document.body.append(canvas);
e.start();

// Recall: the page comes back exactly; a newer edit by the page wins.
e.destroy(); // or e.ledger.restore() to keep the engine

What it owns

  • ledger.text(node, value)

    A text node's value: the 0s and 1s a shot leaves in a word. @fzlt/core/board-page writes into the page's own Text nodes this way, with no wrappers or clones.

  • ledger.style(el, prop, value) and unstyle

    One inline style property: a transform, a custom property. A style attribute comes back byte for byte. unstyle puts one property back now and forgets it.

  • ledger.attr(el, name, value)

    An attribute, set or removed (null): aria-busy, data-hp.

  • ledger.cls(el, name, on)

    One class, added or removed: smashed, burning.

  • ledger.add(node)

    A node the engine put on the page (a canvas, an overlay, debris). Recall removes it.

Three rules

  • Change-only. Writing the value that is already there makes no mutation. A browser test writes the same value twice and counts zero.
  • Host wins. If the host page changed something after the engine did, the ledger stops writing it and never restores over it. A write returns false when the host got there first.
  • Exact. After destroy() the DOM matches a snapshot taken before the engine started, attribute for attribute and style text byte for byte. One AbortSignal also removes every listener and observer.

engine.ledger.restore() is Recall without destroying the engine; the Recall key on the home page demo runs exactly that. Bricks that own an element (the touch pad, the perf overlay, the match overlay) add it through the ledger, so engine.destroy() takes them too.

Questions

Why not just reload the page?

Because the page is someone else's. A reload loses their state, their scroll and their form fields. Recall undoes only what the engine did, and leaves everything else as the visitor left it.

What if my code writes the DOM directly?

Then Recall can't give it back. Write through engine.ledger (or a brick that does) in render, and the page comes back exactly.