# 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