# DreamStack: Reinventing the UI from First Principles > *What if we threw away every assumption about web UI frameworks and started over — with the sole goal of creating ultra-reactive, dynamic interfaces?* --- ## Implementation Status ✅ DreamStack is **real and running** — 8 Rust crates, 205 tests, 51 compilable examples, 14 registry components, ~7KB runtime. ``` .ds source → ds-parser → ds-analyzer → ds-codegen → JavaScript ↓ ↓ ds-types ds-layout (type checker) (Cassowary solver) ``` ### What Works Today | Feature | Syntax | Status | |---------|--------|--------| | Signals | `let count = 0` | ✅ Fine-grained, auto-tracked | | Derived | `let doubled = count * 2` | ✅ `DS.derived()`, lazy evaluation | | Interpolation | `"Count: {count}"` | ✅ Reactive `DS.effect()` | | Conditional | `when count > 5 -> text "hi"` | ✅ Mount/unmount | | If/else | `if x then a else b` | ✅ | | Match | `match state \| Loading -> ...` | ✅ | | List rendering | `for item in items -> text item` | ✅ Reactive, keyed | | Components | `component Card(title) = ...` | ✅ Reactive props (getter fns) | | Component import | `import { Badge } from "./badge"` | ✅ Recursive resolution | | Effects | `effect fetch(id): Result` / `perform` | ✅ Algebraic | | Springs | `spring(target: 0, stiffness: 300)` | ✅ RK4 physics | | Layout | `layout { sidebar.width == 250 }` | ✅ Cassowary solver | | Types | `type PositiveInt = Int where x > 0` | ✅ Refinement types | | Router | `route "/path" -> body` / `navigate` | ✅ Hash-based | | Two-way binding | `input { bind: name }` | ✅ Signal ↔ input | | Async resources | `DS.resource()` / `DS.fetchJSON()` | ✅ Loading/Ok/Err | | Physics scene | `scene { gravity_y: g } [ circle {...} ]` | ✅ Rapier2D WASM | | Constraints | `constrain el.width = expr` | ✅ Reactive solver | | Streaming (signal) | `stream on "ws://..." { mode: signal }` | ✅ Bidirectional, versioned diffs | | Streaming (receiver) | `stream from "ws://..."` | ✅ Auto-reconnect, field select | | Streaming (WebRTC) | `transport: webrtc` | ✅ P2P data channel + WS fallback | | Stream output filter | `output: paddleX, ballY, score` | ✅ Skip internal signals | | Dev server | `dreamstack dev app.ds` | ✅ File watcher + HMR polling | | Playground | `dreamstack playground` | ✅ Monaco editor + live preview | | CLI check | `dreamstack check app.ds` | ✅ Signal graph visualization | | TSX converter | `dreamstack convert component.tsx` | ✅ React → DreamStack | | Component registry | `dreamstack add badge` | ✅ 14 components | | Timer merging | `every 33 -> ...` (multiple) | ✅ Single `setInterval` per interval | | Sound | `play_tone(440, 60)` | ✅ Built-in oscillator | ### CLI Commands ```bash dreamstack build app.ds # Compile to HTML+JS dreamstack dev app.ds # Dev server with hot reload (port 3000) dreamstack check app.ds # Analyze signal graph, type check dreamstack stream app.ds # Compile + serve with streaming dreamstack playground # Monaco editor playground (port 4000) dreamstack add badge # Add a registry component dreamstack add --list # List all available components dreamstack convert file.tsx # Convert React/TSX → DreamStack dreamstack convert button --shadcn # Convert from shadcn/ui registry dreamstack init my-app # Initialize new project ``` ### Registry Components (14) | Component | File | Description | |-----------|------|-------------| | Alert | `alert.ds` | Status messages with variants | | Avatar | `avatar.ds` | Profile images with sizes | | Badge | `badge.ds` | Status indicators (success/warning/error/info) | | Button | `button.ds` | Click actions with variants | | Card | `card.ds` | Content containers | | Dialog | `dialog.ds` | Modal overlays | | Input | `input.ds` | Text inputs with binding | | Progress | `progress.ds` | Progress bars | | Select | `select.ds` | Dropdown selects | | Separator | `separator.ds` | Visual dividers | | Stat | `stat.ds` | Statistic displays (value, label, trend) | | Tabs | `tabs.ds` | Tabbed navigation | | Toast | `toast.ds` | Notification toasts | | Toggle | `toggle.ds` | On/off switches | ### Examples (48 compilable .ds files) **Games:** - `game-pong.ds` — Multiplayer Pong with keyboard, sound, streaming - `game-breakout.ds` — Breakout with 5 rows, collision, score, streaming - `game-snake.ds` — Snake game - `game-reaction.ds` — Reaction time test **Streaming:** - `streaming-counter.ds` — Counter synced across tabs - `streaming-clock.ds` — Clock streamed to viewers - `streaming-dashboard.ds` — Real-time dashboard via relay - `streaming-stats.ds` — Signal stats viewer - `streaming-mood.ds` — Collaborative mood board - `streaming-physics.ds` — Physics scene streaming - `streaming-webrtc.ds` — P2P WebRTC streaming - `streaming-receiver.ds` — Generic stream receiver - `pong-viewer.ds` — Spectator view for Pong - `game-viewer.ds` — Generic game viewer **Audio:** - `step-sequencer.ds` — 16-step drum machine with Web Audio API - `beats-viewer.ds` — Spectator view for step sequencer **UI Patterns:** - `counter.ds` — 3-line counter - `list.ds` — Dynamic list with add/remove - `todo.ds` / `todomvc.ds` — TodoMVC - `form.ds` — Form with validation - `router-demo.ds` — Multi-page routing - `dashboard.ds` — Dashboard layout - `component-gallery.ds` — Component showcase - `showcase.ds` — Full feature showcase - `project-manager.ds` — Kanban-style project tracker - `springs.ds` — Spring physics animations **Language Features:** - `language-features.ds` — Comprehensive syntax demo - `refined-types.ds` — Refinement type guards - `import-demo.ds` — Component imports - `slot-demo.ds` — Component children/slots - `each-demo.ds` — `each` loop rendering - `when-else-demo.ds` — Conditional rendering - `multi-action.ds` / `timer-multi-action.ds` — Multi-statement handlers - `callback-demo.ds` — Callback props - `physics.ds` — Rapier2D physics scene - `bench-signals.ds` — Signal performance benchmarks ### DreamStack vs React | | **DreamStack** | **React** | |---|---|---| | Reactivity | Fine-grained signals, surgical DOM | VDOM diff, re-render subtrees | | State | `count += 1` direct | `setState(c => c+1)` immutable | | Derived | `let d = count * 2` auto | `useMemo(() => ..., [deps])` manual | | Effects | Auto-tracked, algebraic | `useEffect(..., [deps])` manual | | Conditional | `when x -> text "y"` | `{x && y}` | | Lists | `for item in items -> ...` | `{items.map(i => ...)}` | | Router | `route "/path" -> body` | `react-router` (external) | | Forms | `input { bind: name }` | `useState` + `onChange` (manual) | | Animation | Built-in springs | framer-motion (external) | | Physics | Built-in Rapier2D scene | matter.js (external) | | Layout | Built-in Cassowary | CSS only | | Streaming | Built-in bidirectional | WebSocket libraries (external) | | Types | Native refinement types | TypeScript (external) | | Bundle | **~7KB** | **~175KB** | | Ecosystem | 48 examples, 14 components | Massive | ### Benchmarks (signal propagation) | Benchmark | Ops/sec | Signals | |-----------|---------|---------| | Wide Fan-Out (1→1000) | 46K | 1,001 | | Deep Chain (100 deep) | 399K | 100 | | Diamond Dependency | 189K | 4 | | Batch Update (50) | 61K | 50 | | Mixed Read/Write | 242K | 10 | --- ## Quick Start ### Counter (3 lines of logic) ``` let count = 0 view counter = column [ text "Count: {count}" button "+" { click: count += 1 } ] ``` ### Multiplayer Pong (40 lines) ``` let ballX = 300 let ballY = 200 let bvx = 4 let bvy = 3 let p1y = 180 on keydown(ev) -> p1y = if ev.key == "ArrowUp" then p1y - 30 else p1y every 33 -> ballX = ballX + bvx every 33 -> ballY = ballY + bvy every 33 -> bvx = if ballX > 590 then -4 else bvx every 33 -> bvy = if ballY < 5 then 3 else bvy stream pong on "ws://localhost:9100/peer/pong" { mode: signal, output: ballX, ballY, p1y } view pong = stack { style: "position:relative; width:600px; height:400px; background:#1a1a2e" } [ column { style: "...ball styles...", top: ballY, left: ballX } [] column { style: "...paddle...", top: p1y } [] ] ``` ### Spectator View (5 lines) ``` let game = stream from "ws://localhost:9100/peer/pong" select [ballX, ballY, p1y] view viewer = stack { style: "..." } [ column { style: "...ball...", top: game.ballY, left: game.ballX } [] column { style: "...paddle...", top: game.p1y } [] ] ``` ### Run It ```bash # Install cargo install --path compiler/ds-cli # Build dreamstack build examples/counter.ds -o dist # Dev server (with hot reload) dreamstack dev examples/game-pong.ds # With streaming cargo run -p ds-stream # Tab 1: start relay dreamstack dev examples/game-pong.ds # Tab 2: play open dist/index.html # Tab 3: spectate (pong-viewer.ds) ``` --- ## Architecture ### Compiler Pipeline ``` ┌─────────────┐ .ds source ───────►│ ds-parser │──► AST (Program) └──────┬──────┘ │ ┌──────▼──────┐ │ ds-analyzer │──► SignalGraph (DAG) └──────┬──────┘ + DomBindings │ + SignalManifest ┌──────▼──────┐ │ ds-codegen │──► Single-file HTML+JS └──────┬──────┘ (~7KB runtime) │ ┌──────▼──────┐ │ ds-cli │──► dev/build/stream/check └─────────────┘ ``` ### Signal Graph (Compile-Time) ``` Signal: count (Source) ├──► Derived: doubled (DS.derived) │ └──► [DOM] TextNode "Doubled: {doubled}" ├──► [DOM] TextNode "Count: {count}" └──► [Stream] _streamDiff("count", count.value) ``` - **No virtual DOM.** Compiler maps signals → specific DOM nodes at build time - **No re-rendering.** Components execute once, set up reactive bindings - **No dependency arrays.** Compiler infers all dependencies from code - **Timer merging.** All `every 33` statements share one `setInterval` ### Reactive Runtime ```javascript // Source signal const count = DS.signal(0); // Derived signal (auto-updates) const doubled = DS.derived(() => count.value * 2); // Effect (auto-tracked, re-runs on dependency change) DS.effect(() => { el.textContent = `Count: ${count.value}`; }); // Component props: getter functions for live reactivity DS_Badge({ label: () => `Score: ${score.value}` }); ``` ### Stream Protocol (16-byte binary header) ``` ┌────────┬─────────┬──────────┬────────────┬───────┬────────┬────────┐ │ type │ flags │ seq │ timestamp │ width │ height │ length │ │ u8 │ u8 │ u16 │ u32 │ u16 │ u16 │ u32 │ └────────┴─────────┴──────────┴────────────┴───────┴────────┴────────┘ ``` | Mode | What's Sent | Bandwidth | |------|-------------|-----------| | `signal` (default) | JSON diffs of changed signals | ~2 KB/s | | `delta` | XOR + RLE compressed pixel deltas | ~50 KB/s | | `pixel` | Raw RGBA framebuffer | ~30 MB/s | Features: - **Bidirectional sync** with per-signal version counters - **Output filtering** — only listed signals are broadcast - **Exponential reconnect** — 2s → 4s → 8s, max 30s, reset on success - **WebRTC data channel** with WebSocket fallback - **Deduplication** — skip unchanged values --- ## Vision React was revolutionary in 2013. But it carries a decade of compromises: the virtual DOM, hooks with manual dependency arrays, re-rendering entire subtrees, CSS from 1996, animations bolted on as an afterthought. DreamStack asks: **what would we build today if none of that existed?** The answer is a unified system where **UI is data, reactivity is automatic, effects are composable values, layout is constraint-based, animation is physics-native, and the editor and runtime are one.** --- ## The Language Not Clojure. Not TypeScript. A new language that steals the best ideas from everywhere. ### Core Properties | Property | Inspiration | Why | |---|---|---| | **Homoiconic** | Clojure, Lisp | UI = data. Code = data. Everything is transformable | | **Refinement types** | Liquid Haskell | `type Percent = Int where value >= 0 and value <= 100` | | **Algebraic effects** | Eff, Koka, OCaml 5 | Side effects as first-class, composable, interceptable values | | **Reactive by default** | Svelte, Solid, Excel | No `useState`, no subscriptions. Assignment *is* the API | | **Structural typing** | TypeScript, Go | Flexible composition without class hierarchies | | **Compiled to JS** | Svelte, Solid | Near-zero runtime overhead | ### Syntax Reference ``` -- Signals (mutable state) let count = 0 let name = "world" let items = [1, 2, 3] -- Derived signals (auto-updated) let doubled = count * 2 let greeting = "Hello, {name}!" -- Refinement types type PositiveInt = Int where value > 0 let score: PositiveInt = 10 -- Event handlers on keydown(ev) -> count = if ev.key == "ArrowUp" then count + 1 else count -- Timers every 33 -> ballX = ballX + velocity -- Conditional rendering when count > 10 -> text "High!" -- Pattern matching match state "loading" -> spinner "error" -> text "Failed" _ -> text "Ready" -- For loops for item, i in items -> text "{i}: {item}" -- Components component Card(title, children) = column { variant: "card" } [ text title { variant: "card-title" } slot ] -- Imports import { Badge, Card } from "../registry/components/badge" -- Routes route "/" -> text "Home" route "/about" -> text "About" -- Springs let x = spring(target: 0, stiffness: 300, damping: 30) -- Streaming stream app on "ws://localhost:9100/peer/app" { mode: signal, output: count, name } -- Receiving streams let remote = stream from "ws://localhost:9100/peer/app" select [count, name] -- Views view main = column [ text "Hello {name}" button "Click" { click: count += 1 } ] ``` --- ## Effect System: Algebraic Effects ``` effect fetchUser(id: UserId): Result view profile(id: UserId) = let user = perform fetchUser(id) match user Loading -> skeleton-loader Ok(u) -> column [ avatar u.photo, text u.name ] Err(e) -> error-card e | with retry: perform fetchUser(id) ``` --- ## Layout: Constraint-Based ``` layout dashboard { sidebar.width == 250 main.x == sidebar.x + sidebar.width main.width == parent.width - sidebar.width [strong] } ``` Uses a Cassowary constraint solver. Constraints are reactive — when viewport changes, layout re-solves in a single pass. --- ## Animation: Physics-Based Springs ``` let panel_x = spring(target: 0, stiffness: 300, damping: 30) on toggle_sidebar -> panel_x.target = if open then 250 else 0 view sidebar = panel { x: panel_x } [ nav-items ] ``` Springs are interruptible, gesture-driven, and composable through the same signal system. --- ## Physics: Rapier2D WASM ``` let gravity_y = 980 view main = scene { width: 700, height: 450, gravity_y: gravity_y } [ circle { x: 200, y: 80, radius: 35, color: "#8b5cf6" } rect { x: 500, y: 100, width: 80, height: 50, color: "#10b981" } ] button "Anti-Gravity" { click: gravity_y = -500 } ``` --- ## Streaming: Built-In Multiplayer Any DreamStack app becomes multiplayer with one declaration: ``` stream pong on "ws://localhost:9100/peer/pong" { mode: signal, output: ballX, ballY, paddleY, score } ``` The compiler automatically: 1. Connects to the relay via WebSocket 2. Wraps signal mutations with `_streamDiff()` calls 3. Sends versioned JSON diffs (conflict resolution via version counters) 4. Filters to only broadcast `output` signals (skips internals like velocity) 5. Reconnects with exponential backoff on disconnect Viewers connect with `stream from`: ``` let game = stream from "ws://localhost:9100/peer/pong" select [ballX, ballY, paddleY, score] ``` --- ## Comparison | Capability | React | Svelte 5 | Solid.js | Flutter | **DreamStack** | |---|---|---|---|---|---| | Reactivity | Pull (VDOM diff) | Runes | Signals | Rebuild | **Compile-time DAG** | | Side effects | `useEffect` + deps | `$effect` | `createEffect` | Lifecycle | **Algebraic effects** | | Layout | CSS (external) | CSS (scoped) | CSS (external) | Constraints | **Cassowary (native)** | | Animation | 3rd party | 3rd party | 3rd party | Physics (native) | **Springs (native)** | | Streaming | WebSocket libs | WebSocket libs | WebSocket libs | None | **Built-in bidirectional** | | Runtime | ~175KB | ~2KB | ~7KB | ~2MB | **~7KB** | | Type safety | TypeScript | TypeScript | TypeScript | Dart | **Refinement types** | | UI = Data | No (JSX) | No (templates) | No (JSX) | No (widgets) | **Yes (homoiconic)** | --- ## Design Philosophy 1. **The compiler is the framework.** Move work from runtime to compile time 2. **Reactivity is the default.** Every value is live. You opt *out*, not *in* 3. **Effects are values.** First-class, composable, interceptable 4. **Layout and animation are core.** Not CSS bolt-ons or third-party libraries 5. **The editor and runtime are one.** Bidirectional structural editing 6. **UI is data, all the way down.** If you can't `map` over your UI, your abstraction is wrong 7. **Any bitstream in → any bitstream out.** The UI is just one codec --- ## Next Steps ### Phase 1: Polish (estimated ~2h) - [ ] **Better error messages** — source context with line + caret in parser errors - [ ] **Route integration test** — multi-page example exercising `route` + `navigate` - [ ] **WebRTC integration test** — verify P2P data channel transport - [ ] **Layout constraint test** — example using Cassowary solver ### Phase 2: New Examples (estimated ~2h) - [ ] **Tetris** — stress-test grid, timers, collision, streaming - [ ] **Todo with sync** — CRUD + bidirectional streaming sync - [ ] **Snake polish** — cleanup existing game, add streaming ### Phase 3: Ecosystem (estimated ~1.5h) - [ ] **Component gallery** — live showcase of all 14 components - [ ] **Crates.io publish** — `cargo install dreamstack` - [ ] **Self-hosted playground** — deploy playground as web app