- Add scene container to AST, lexer, parser, analyzer, and codegen
- Add circle/rect/line as UI elements for physics body declaration
- Compile scene {} to canvas + async WASM init + Rapier2D PhysicsWorld
- Reactive gravity via DS.effect() — bodies wake on gravity change
- Mouse drag interaction with impulse-based body movement
- Compile-time hex color parsing for body colors
- Fix is_signal_ref matching numeric literals (700.value bug)
- Fix body variable uniqueness (next_node_id per body)
- Fix gravity signal detection (check AST Ident before emit_expr)
- Add physics.ds example with 5 bodies + 4 gravity control buttons
- Update DREAMSTACK.md and IMPLEMENTATION_PLAN.md with Phase 10-11
- 39 tests pass across all crates, 22KB output
149 lines
6.2 KiB
Markdown
149 lines
6.2 KiB
Markdown
# ds-stream — Universal Bitstream Streaming
|
|
|
|
> Any input bitstream → any output bitstream.
|
|
> Neural nets will generate the pixels. The receiver just renders bytes.
|
|
|
|
## What It Does
|
|
|
|
```
|
|
┌──────────┐ WebSocket ┌─────────┐ WebSocket ┌──────────┐
|
|
│ Source │ ───frames────► │ Relay │ ───frames────► │ Receiver │
|
|
│ (renders) │ ◄───inputs──── │ (:9100) │ ◄───inputs──── │ (~300 LOC)│
|
|
└──────────┘ └─────────┘ └──────────┘
|
|
```
|
|
|
|
The **source** runs a DreamStack app (signal graph + springs + renderer), captures output as bytes, and streams it. The **receiver** is a thin client that renders whatever bytes arrive — no framework, no runtime.
|
|
|
|
## Binary Protocol
|
|
|
|
Every message = **16-byte header** + payload.
|
|
|
|
```
|
|
┌──────┬───────┬──────┬───────────┬───────┬────────┬────────────┐
|
|
│ type │ flags │ seq │ timestamp │ width │ height │ payload_len│
|
|
│ u8 │ u8 │ u16 │ u32 │ u16 │ u16 │ u32 │
|
|
└──────┴───────┴──────┴───────────┴───────┴────────┴────────────┘
|
|
```
|
|
|
|
### Frame Types (source → receiver)
|
|
|
|
| Code | Type | Description |
|
|
|------|------|-------------|
|
|
| `0x01` | Pixels | Raw RGBA framebuffer |
|
|
| `0x02` | CompressedPixels | PNG/WebP (future) |
|
|
| `0x03` | DeltaPixels | XOR delta + RLE |
|
|
| `0x10` | AudioPcm | Float32 PCM samples |
|
|
| `0x11` | AudioCompressed | Opus (future) |
|
|
| `0x20` | Haptic | Vibration command |
|
|
| `0x30` | SignalSync | Full signal state JSON |
|
|
| `0x31` | SignalDiff | Changed signals only |
|
|
| `0x40` | NeuralFrame | Neural-generated pixels |
|
|
| `0x41` | NeuralAudio | Neural speech/music |
|
|
| `0x42` | NeuralActuator | Learned motor control |
|
|
| `0x43` | NeuralLatent | Latent space scene |
|
|
| `0xFE` | Ping | Keep-alive |
|
|
| `0xFF` | End | Stream termination |
|
|
|
|
### Input Types (receiver → source)
|
|
|
|
| Code | Type | Payload |
|
|
|------|------|---------|
|
|
| `0x01` | Pointer | x(u16) y(u16) buttons(u8) |
|
|
| `0x02` | PointerDown | same |
|
|
| `0x03` | PointerUp | same |
|
|
| `0x10` | KeyDown | keycode(u16) modifiers(u8) |
|
|
| `0x11` | KeyUp | same |
|
|
| `0x20` | Touch | id(u8) x(u16) y(u16) |
|
|
| `0x30` | GamepadAxis | axis(u8) value(f32) |
|
|
| `0x40` | Midi | status(u8) d1(u8) d2(u8) |
|
|
| `0x50` | Scroll | dx(i16) dy(i16) |
|
|
| `0x60` | Resize | width(u16) height(u16) |
|
|
| `0x90` | BciInput | (future) |
|
|
|
|
### Flags
|
|
|
|
| Bit | Meaning |
|
|
|-----|---------|
|
|
| 0 | `FLAG_INPUT` — message is input (receiver→source) |
|
|
| 1 | `FLAG_KEYFRAME` — full state, no delta |
|
|
| 2 | `FLAG_COMPRESSED` — payload is compressed |
|
|
|
|
## Streaming Modes
|
|
|
|
| Mode | Frame Size | Bandwidth @30fps | Use Case |
|
|
|------|-----------|-------------------|----------|
|
|
| **Pixel** | 938 KB | ~28 MB/s | Full fidelity, any renderer |
|
|
| **Delta** | 50-300 KB | ~1-9 MB/s | Low-motion scenes |
|
|
| **Signal** | ~80 B | ~2 KB/s | DreamStack-native, receiver renders |
|
|
| **Neural** | 938 KB | ~28 MB/s | Model-generated output |
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Start the relay server
|
|
cargo run -p ds-stream
|
|
|
|
# Serve the examples
|
|
python3 -m http.server 8080 --directory examples
|
|
|
|
# Open in browser
|
|
# Tab 1: http://localhost:8080/stream-source.html
|
|
# Tab 2: http://localhost:8080/stream-receiver.html
|
|
```
|
|
|
|
Click mode buttons on the source to switch between Pixel / Delta / Signal / Neural. Toggle audio. Open multiple receiver tabs.
|
|
|
|
## Crate Structure
|
|
|
|
```
|
|
engine/ds-stream/
|
|
├── Cargo.toml
|
|
└── src/
|
|
├── lib.rs # crate root
|
|
├── protocol.rs # types, header, events (412 lines)
|
|
├── codec.rs # encode/decode, delta, builders (247 lines)
|
|
├── relay.rs # WebSocket relay server (255 lines)
|
|
└── main.rs # CLI entry point
|
|
```
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
cargo test -p ds-stream # 17 tests
|
|
```
|
|
|
|
Covers: header roundtrip, event roundtrip, delta compression, frame type enums, flag checks, partial buffer handling, message size calculation.
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
### Near-term
|
|
|
|
1. **WebRTC transport** — Replace WebSocket with WebRTC DataChannel for sub-10ms latency and NAT traversal. The protocol is transport-agnostic; only the relay changes.
|
|
|
|
2. **Opus audio compression** — Replace raw PCM (`AudioPcm`) with Opus encoding (`AudioCompressed`). 28 KB/s → ~6 KB/s for voice, near-transparent quality.
|
|
|
|
3. **Adaptive quality** — Source monitors receiver lag (via ACK frames) and auto-downgrades: full pixels → delta → signal diff. Graceful degradation on slow networks.
|
|
|
|
4. **WASM codec** — Compile `protocol.rs` + `codec.rs` to WASM so source and receiver share the exact same binary codec. No JS reimplementation drift.
|
|
|
|
### Medium-term
|
|
|
|
5. **Persistent signal state** — Source sends `SignalSync` keyframes periodically. New receivers joining mid-stream get full state immediately, then switch to diffs.
|
|
|
|
6. **Touch/gamepad input** — Wire up Touch, GamepadAxis, GamepadButton input types on receiver. Enable mobile and controller interaction.
|
|
|
|
7. **Frame compression** — PNG/WebP encoding for pixel frames (`CompressedPixels`). Canvas `toBlob('image/webp', 0.8)` gives ~50-100 KB/frame vs 938 KB raw.
|
|
|
|
8. **Haptic output** — Receiver calls `navigator.vibrate()` on Haptic frames. Spring impact → buzz.
|
|
|
|
### Long-term (neural path)
|
|
|
|
9. **Train a pixel model** — Collect (signal_state, screenshot) pairs from the springs demo. Train a small CNN/NeRF to predict framebuffer from signal state. Replace `neuralRender()` with real inference.
|
|
|
|
10. **Latent space streaming** — Instead of pixels, stream a compressed latent representation (`NeuralLatent`). Receiver runs a lightweight decoder model. ~1 KB/frame for HD content.
|
|
|
|
11. **Voice input** — Receiver captures microphone audio, streams as `VoiceInput`. Source interprets via speech-to-intent model to drive signals.
|
|
|
|
12. **Multi-source composition** — Multiple sources stream to the same relay. Receiver composites layers. Each source owns a region or z-layer of the output.
|