Skip to content

Overview ​

wity-rkf is the reactive-knowledge layer over wity-graph: reactive knowledge documents modelled as state machines, with a headless, isomorphic reference implementation. It defines element types and evaluation semantics over a knowledge graph; documents are stored as <wity-knowledge> — the same representation wity-graph, wity-workbook and wity-ideation use — so RKF has no wire format of its own. It is the shared reactive layer between:

  • Authoring tools -- a visual editor that produces RKF documents
  • Renderers -- DOM, Canvas, SVG, WebGL, or any other rendering target
  • Notebooks -- interactive explorable explanations with prose, math, and simulations

Core model ​

An RKF document is a pure function of state:

f(document: RkfDocument, state: Object) -> ComputedFrame
  • document is the document model — from parse(), or built directly (immutable during evaluation)
  • state is a plain object of variable values
  • ComputedFrame is the complete render state: resolved marks, controls, envs, gates, outputs, and section tree

No interpolation state, no DOM, no framework, no side effects.

Documents as state machines ​

RKF v2.0 reframes the spec around state machine theory, grounded in three vector spaces derived from first principles:

Feature-space (rkf-var) ​

The dimensions of a machine's state. Each variable is an axis in feature-space:

xml
<rkf-var id="t" type="number" init="0" />
<rkf-var id="running" type="boolean" init="false" />
<rkf-var id="pulse" init="0" elasticity="2" elastic-default="0" />

Variables can have elasticity -- self-resetting temporal behaviour where the variable snaps back to a default value after a delay.

Observation-space (rkf-gate) ​

Named boolean conditions over feature-space. Gates recognise regions in feature-space:

xml
<rkf-gate id="isRunning" expr="running" />
<rkf-gate id="trained" expr="epoch > 100" />

State is emergent, not declared -- it arises from regions in feature-space recognised by gates. The gate attribute on visual elements can reference a gate id or an inline expression.

Transformation-space (rkf-transform) ​

State transitions triggered by gates (or expressions). Replaces the v1.0 rkf-update element:

xml
<rkf-transform trigger="isRunning" on="tick">
  <rkf-set var="t" expr="t + 1" />
</rkf-transform>

The trigger attribute accepts a gate id or an inline expression. The optional on attribute filters by event name.

Document structure ​

RkfDocument
├── RkfVar[]            (document-level feature variables)
└── RkfSection[]        (top-level sections; optional gate)
    ├── RkfEnv          (opaque environments: prose, math, image, video, model)
    └── RkfMachine      (state machine blocks)
        ├── RkfVar[]    (feature-space: state dimensions)
        ├── RkfGate[]   (observation-space: named conditions)
        ├── RkfDerive[] (derived features)
        ├── RkfTransform[] (transformation-space: state transitions)
        │   └── RkfSet[]   (variable assignments)
        ├── RkfPort[]   (typed I/O interfaces, dir=in|out)
        ├── RkfMachine[] (nested sub-machines, recursive)
        ├── RkfCanvas   (visual output area)
        │   ├── RkfScale[]  (data-to-pixel mapping)
        │   ├── RkfAxis[]   (axis descriptors)
        │   └── Marks[]     (scatter, line, arrow, rect, circle, arc, text, path, area)
        └── RkfEnv[]    (opaque environments with optional ports)

State model ​

State is the primary axis (unlike wity-scene, where time is primary).

Variables (rkf-var) declare feature-space dimensions with typed initial values:

xml
<rkf-var id="learning_rate" type="number" init="0.1" />
<rkf-var id="mode" type="string" init="linear" />
<rkf-var id="running" type="boolean" init="false" />
<rkf-var id="pulse" init="0" elasticity="2" elastic-default="0" />

Derived values (rkf-derive) compute from variables using expressions:

xml
<rkf-derive id="slope" expr="-w1 / w2" />
<rkf-derive id="intercept" expr="-bias / w2" />

Derives are resolved in topological (dependency) order, not declaration order. Each machine's variables, derives and gates form a typed dependency graph; a derive that references another derive is always evaluated after its dependency, regardless of the order they were authored in. Combinational cycles among derives/gates are illegal and reported by validate(). Transform feedback (t = t + 1) is not a combinational cycle — it flows across time via rkf-transform, so it is permitted (see Dependency graph).

Gates (rkf-gate) declare named boolean conditions over feature-space:

xml
<rkf-gate id="isRunning" expr="running" />
<rkf-gate id="converged" expr="error < 0.01" />

Transforms (rkf-transform) define state transitions triggered by gates:

xml
<rkf-transform trigger="isRunning" on="tick">
  <rkf-set var="epoch" expr="epoch + 1" />
  <rkf-set var="error" expr="computeError(weights, data)" />
</rkf-transform>

Gate attributes control conditional visibility on any element, referencing a gate id or an inline expression:

xml
<rkf-env type="prose" gate="mode == 'linear'">In linear mode, the output is unbounded.</rkf-env>
<rkf-group gate="epoch > 0">
  <rkf-text x="10" y="10" text="Training started" />
</rkf-group>

Recursive composition ​

rkf-machine elements can contain nested rkf-machine elements. Inner variables are accessible via dot notation:

xml
<rkf-machine id="outer">
  <rkf-machine id="sub">
    <rkf-var id="count" type="number" init="0" />
  </rkf-machine>
  <rkf-derive id="total" expr="sub.count * 2" />
</rkf-machine>

Cross-representation composition ​

Beyond nesting machines, RKF environments can embed other <wity-knowledge> representations inline — workbooks, system maps, decision frameworks. Each embedded representation is stored as a snapshot within the RKF document's knowledge graph and rendered by its own domain editor.

This makes an RKF document a composite document: prose, math, reactive state machines, and live domain editors coexist in a single serializable knowledge graph. See the Composition guide for details.

Rendering targets ​

wity-rkf is rendering-target-agnostic. evaluate() returns resolved coordinates and props. The renderer decides what to do with them:

TargetHow
SVGMap marks to SVG elements, ports to HTML form elements
Canvas 2DDraw marks using fillRect, arc, moveTo/lineTo, fillText
DOM/HTMLPosition elements with CSS, render envs as HTML
WebGL/Three.jsMap marks to 3D scene objects
Server-sideEvaluate for testing, validation, or static rendering

Expression evaluator ​

RKF includes a safe, hand-rolled recursive descent expression evaluator. No eval(), no Function().

Supports:

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, <, >, <=, >=
  • Logical: &&, ||, !
  • Ternary: cond ? a : b
  • Parentheses: (expr)
  • Math functions: Math.abs, Math.floor, Math.ceil, Math.round, Math.min, Math.max, Math.sqrt, Math.sin, Math.cos, Math.tan, Math.PI
  • Dot access: obj.field

Dependency graph ​

Each machine's feature-space (rkf-var), derived features (rkf-derive) and observation-space (rkf-gate) form a typed, directed dependency graph:

  • nodes — vars, derives, gates
  • depends-on edges — from each referenced id to the derive/gate that reads it (combinational dataflow, must be acyclic)
  • reads / writes edges — transform inputs/outputs, which may close a cycle across time

This graph is the substrate for two guarantees:

  1. Order-independent evaluation. Derives resolve in topological order, so authoring order never affects results.
  2. Structural cycle detection. validate() reports illegal combinational cycles among derives/gates, while leaving legal transform feedback (t = t + 1) untouched — the two are distinguished by edge type.

Documents are <wity-knowledge> graphs ​

RKF has no serialization of its own. An RKF document is a knowledge graph — every element is a typed node, every reference a typed edge — and its canonical stored form is a <wity-knowledge> document, exactly as wity-workbook stores spreadsheets as knowledge graphs. parse() and serialize() delegate to graph-headless, which is injected once at startup:

js
import * as graph from '@wity/graph-headless';
import { setGraphProvider, setXmlParser } from '@wity-ai/rkf-core';

setGraphProvider(graph);           // required for parse() / serialize()
setXmlParser(new DOMParser());     // Node.js only; forwarded to graph-headless

evaluate(), validate() and applyUpdate() operate on an already-parsed document and need no injection. Documents authored in the retired <wity-rkf> format upgrade via rkfToKnowledge(oldXml).

This is the substrate that makes RKF the reactive interpretation layer over a knowledge graph — wity-graph answers "what is true," wity-rkf answers "what happens next," over the same <wity-knowledge> representation.

Relation to wity-scene ​

wity-scenewity-rkf
Primary axisTime (t in seconds)State (variable values)
Core functionf(scene, t) -> ComputedFramef(document, state) -> ComputedFrame
Content modelLayers with positioned visual elementsSections with envs, machines, ports
Use caseMotion graphics, title cards, video overlaysInteractive notebooks, explorable explanations
Shared patternsDeclarative format, headless evaluation, isomorphic, zero depsSame