Skip to content

Latest commit

 

History

256 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cliffy

npm Netlify Status License: Apache 2.0 Rust WASM

A WASM-first reactive framework with classical FRP semantics, powered by geometric algebra.

Live Examples | Documentation | API Reference

Build collaborative applications at scale where distributed systems problems become geometric algebra problems with closed-form solutions and guaranteed convergence.

Install

npm install @cliffy-ga/core

Quick Start

# Create a new Cliffy project
npx create-cliffy my-app

# Or specify a template
npx create-cliffy my-app --template typescript-vite  # TypeScript + Vite (default)
npx create-cliffy my-app --template bun              # Bun runtime
npx create-cliffy my-app --template purescript       # PureScript + type-safe DSL

cd my-app
npm install
npm run dev

Features

  • Classical FRP - Behavior<T> (continuous) and Event<T> (discrete) following Conal Elliott's original semantics
  • Algebraic TSX - Declarative UI with html tagged templates that auto-update when Behaviors change
  • WASM-First - Core logic in Rust, runs anywhere via WebAssembly
  • Distributed State - Geometric CRDTs with lattice join, vector clocks, and coordination-free merge
  • Geometric Foundation - State transformations as geometric operations (hidden from users)
  • Multi-Language - TypeScript, JavaScript, and PureScript bindings
  • No WASM? No problem - cliffy-tsukoshi provides pure TypeScript geometric state for mobile and constrained environments

Algebraic TSX

Cliffy uses Algebraic TSX for rendering - a declarative approach where Behaviors automatically update the DOM:

TypeScript (html tagged template)

import init, { behavior, combine } from '@cliffy-ga/core';
import { html, mount } from '@cliffy-ga/core/html';

async function main() {
  await init();

  // Create reactive state
  const count = behavior(0);
  const doubled = count.map(n => n * 2);

  // Event handlers
  const increment = () => count.update(n => n + 1);
  const decrement = () => count.update(n => n - 1);

  // Behaviors in templates automatically update the DOM
  const app = html`
    <div class="counter">
      <h1>Count: ${count}</h1>
      <button onclick=${decrement}>-</button>
      <button onclick=${increment}>+</button>
      <p>Doubled: ${doubled}</p>
    </div>
  `;

  mount(app, '#app');
}

main();

PureScript (type-safe Html DSL)

import Cliffy (behavior, update)
import Cliffy.Html (div, h1_, button, text, behaviorText, mount)
import Cliffy.Html.Attributes (className)
import Cliffy.Html.Events (onClick)

counter :: Effect Html
counter = do
  count <- behavior 0

  pure $ div [ className "counter" ]
    [ h1_ [ text "Count: ", behaviorText count ]
    , button [ onClick \_ -> update (_ + 1) count ] [ text "+" ]
    , button [ onClick \_ -> update (_ - 1) count ] [ text "-" ]
    ]

Vanilla JavaScript

import init, { behavior, event } from '@cliffy-ga/core';

await init();

const count = behavior(0);
count.subscribe(n => {
  document.getElementById('count').textContent = n;
});

document.getElementById('increment').onclick = () => {
  count.update(n => n + 1);
};

Core Concepts

Behaviors (Time-Varying Values)

A Behavior<T> represents a value that changes over time - like a spreadsheet cell that updates automatically.

const count = behavior(0);
count.sample();              // Get current value: 0
count.set(10);               // Set directly
count.update(n => n + 1);    // Transform: 11

// Derived behaviors update automatically
const doubled = count.map(n => n * 2);  // 22

// Combine multiple behaviors
const sum = combine(a, b, (x, y) => x + y);

Events (Discrete Occurrences)

An Event<T> represents discrete occurrences over time - like button clicks or network responses.

const clicks = event<MouseEvent>();

clicks.subscribe(e => console.log('Clicked at', e.clientX));
clicks.emit(mouseEvent);

// Transform events
const xPositions = clicks.map(e => e.clientX);
const leftClicks = clicks.filter(e => e.button === 0);

// Accumulate into Behavior
const clickCount = clicks.fold(0, (acc, _) => acc + 1);

Combinators

// Combine multiple behaviors
const width = behavior(10);
const height = behavior(5);
const area = combine(width, height, (w, h) => w * h);
const volume = combine3(w, h, d, (w, h, d) => w * h * d);

// Conditional rendering
const show = behavior(true);
const message = when(show, () => 'Visible!');

// if/else for behaviors
const text = ifElse(isLoading, () => "Loading...", () => "Ready");

Examples

Example Description Run
tsx-counter Basic counter with derived state npm run dev -w tsx-counter
tsx-todo Todo list with filtering npm run dev -w tsx-todo
tsx-forms Form validation patterns npm run dev -w tsx-forms
whiteboard Collaborative drawing canvas npm run dev -w whiteboard
design-tool Shape manipulation with rotors npm run dev -w design-tool
multiplayer-game Entity interpolation with latency sim npm run dev -w multiplayer-game
document-editor CRDT-based collaborative editing npm run dev -w document-editor
p2p-sync P2P sync with network partitions npm run dev -w p2p-sync
crdt-playground Interactive CRDT exploration npm run dev -w crdt-playground
geometric-transforms Rotor rotations visualized npm run dev -w geometric-transforms
gpu-benchmark WebGPU vs CPU performance npm run dev -w gpu-benchmark
testing-showcase Algebraic testing patterns npm run dev -w testing-showcase
purescript-counter Counter in PureScript See example README
purescript-todo Todo list in PureScript See example README

Project Structure

cliffy/
├── cliffy-core/           # Rust FRP implementation (85 tests)
│   └── src/
│       ├── behavior.rs    # Behavior<T> - continuous signals
│       ├── event.rs       # Event<T> - discrete occurrences
│       ├── combinators.rs # when, ifElse, combine
│       ├── component.rs   # Component model
│       ├── dataflow.rs    # Dataflow graph IR
│       └── geometric.rs   # GA conversion (internal)
├── cliffy-wasm/           # WASM bindings (@cliffy-ga/core on npm)
│   ├── src/
│   │   ├── lib.rs         # WASM exports
│   │   └── protocols.rs   # CRDT/VectorClock bindings
│   └── pkg/
│       ├── html.ts        # Algebraic TSX implementation
│       └── cliffy_wasm.js
├── cliffy-tsukoshi/       # Pure TypeScript geometric state (zero deps)
│   └── src/
│       ├── ga3.ts         # GA3 multivector operations
│       ├── rotor.ts       # Rotations with SLERP
│       ├── transform.ts   # Rotation + translation
│       └── state.ts       # GeometricState + ReactiveState
├── cliffy-purescript/     # PureScript bindings
│   └── src/
│       ├── Cliffy.purs    # FRP primitives (Behavior, Event)
│       └── Cliffy/
│           ├── Html.purs  # Type-safe Html DSL
│           └── Foreign.js # FFI bridge
├── cliffy-protocols/      # Distributed state (42 tests)
├── cliffy-gpu/            # WebGPU/SIMD acceleration (18 tests)
├── cliffy-test/           # Algebraic testing framework (30 tests)
├── cliffy-loadtest/       # Scale testing simulator (15 tests)
├── tools/
│   └── create-cliffy/     # Project scaffolding CLI
├── examples/              # 14 example applications
└── docs/                  # Documentation

cliffy-tsukoshi

For environments without WASM support (mobile apps, edge functions, etc.), cliffy-tsukoshi provides the geometric state management core as pure TypeScript:

import { GeometricState, Rotor, ReactiveState } from 'cliffy-tsukoshi';

// Smooth interpolation between states
const current = GeometricState.fromVector(0, 0, 0);
const target = GeometricState.fromVector(100, 50, 0);
const midway = current.blend(target, 0.5);  // (50, 25, 0)

// Rotations via rotors
const rotate90 = Rotor.fromAxisAngle('xy', Math.PI / 2);
const rotated = current.applyRotor(rotate90);

// Reactive wrapper with subscriptions
const state = new ReactiveState(current);
state.subscribe(s => updateUI(s));
state.blendTo(target, 0.3);

~530 lines, zero dependencies, 64 tests. See cliffy-tsukoshi/README.md for full documentation.

Building from Source

Prerequisites

  • Rust (stable)
  • wasm-pack (cargo install wasm-pack)
  • Node.js 18+

Build

npm run build          # Build WASM + post-process
npm run build:release  # Optimized release build
npm run dev            # Watch mode for development

Test

# Run all tests
cargo test --workspace  # 250 tests

# Run specific crate tests
cargo test -p cliffy-core
cargo test -p cliffy-protocols

Development Server

# Run an example
cd examples/tsx-counter
npm install
npm run dev

Why Geometric Algebra?

Cliffy uses Clifford Algebra (GA3 = Cl(3,0)) internally to represent state. This provides:

  • Unified representation: Scalars, vectors, and higher-grade elements in one structure
  • Natural transformations: Rotations, translations, scaling as algebraic operations
  • Conflict resolution: Geometric mean provides coordination-free CRDT merging
  • Mathematical elegance: Clean composition of transformations

You never need to know this. The geometric algebra is purely an implementation detail. The public API exposes familiar FRP primitives.

Documentation

Roadmap

See ROADMAP.md for the full development plan.

Phase Status Description
Phase 0 Done Algebraic Testing Framework
Phase 1 Done Geometric State Foundation
Phase 2 Done Distributed State (CRDT)
Phase 3 Planned Synchronization (WebRTC, persistence)
Phase 4 Done Algebraic TSX Components
Phase 5 Done Edge Computing (WebGPU)
Phase 6 v0.3.0 Production Readiness
Phase 7 Planned Native Mobile (Trebek)

License

Apache-2.0. See LICENSE. Industrial Algebra is the copyright holder; contributions are made under the CLA.

About

Geometric Functional Reactive UI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages