Machine-translated draft — terminology + flow still being reviewed.

Keel Snack

Status: scaffold shipped; cloud-streamed runtime planned for later.

Browser playground for Keel React Native snippets — paste TS in the left pane, see it running on a real emulator streamed back to your tab. Or scan a QR with Keel Go to preview on your phone.

Two preview surfaces, one session

                       ┌──────────────────────────────────┐
                       │  Browser                          │
                       │  ┌──────────┐ ┌────────────────┐ │
                       │  │ Monaco   │ │ <video> tag    │ │
                       │  │ editor   │ │  ← WebRTC      │ │
                       │  │          │ │                │ │
                       │  └─────┬────┘ └────────────────┘ │
                       │        │                          │
                       │   save │            ▲ QR code     │
                       └────────┼────────────┼─────────────┘
                                │            │
                                ▼            │
                ┌──────────────────────────────────────────┐
                │  Snack session server                    │
                │  ┌────────────────────────────────────┐  │
                │  │ Emulator pool (iOS / Android)       │  │
                │  │  + Snack Runtime app pre-installed  │  │
                │  │  + ffmpeg → WebRTC peer connection  │  │
                │  └────────────────────────────────────┘  │
                │                ▲                         │
                │       reload on │ code save              │
                │                │                         │
                │   Snack Runtime app (Keel-built):        │
                │   - subscribes to /api/session/{id}/feed │
                │   - hot-reloads bundle on every change   │
                └──────────────────────────────────────────┘

                                │ same /api/session/{id} URL

                       ┌──────────────────────────────────┐
                       │  Phone (Keel Go scans QR)        │
                       │  → loads the same Snack Runtime  │
                       │     bundle directly on device    │
                       └──────────────────────────────────┘

What you get

SurfaceUse caseFidelity
In-browser emulator (WebRTC video)Quick share — colleague opens URL, sees the app running in seconds without installing anything1:1 with real iOS Simulator / Android Emulator (it IS one, streamed)
Keel Go real-device (QR scan)“Show it on my phone” — touch + sensors + push + WeChat-pay flows that emulators don’t fake wellReal device, real OS

Both surfaces share the same session ID: type code once, watch it reload on the emulator video, hand someone the QR and they see the same thing on their phone within seconds.

Snack Runtime

A specialized Keel app pre-loaded into every emulator + downloadable into Keel Go. It:

  1. Subscribes to /api/session/{id}/feed (SSE) on boot
  2. On every code save, fetches the rebuilt bundle from build-server and hot-reloads via KeelView.loadBundle(uri)
  3. Reports back errors / console.log lines so the Snack browser UI can show them next to the editor

Built from keel/snack/runtime-app/ and shipped as a Keel bundle that runs inside Keel Go (no separate install for QR-scan users).

How a session works

  1. User opens snack.keel.appunvs.com → Next.js app loads Monaco
  2. User types or pastes code, hits Save
  3. Frontend: POST /api/session with the source tree
  4. Snack session server:
    • Allocates an emulator from the pool (iOS or Android per user pref)
    • Starts a WebRTC peer connection back to the browser
    • Generates a short URL (snack.keel.appunvs.com/s/<id>) + QR code
  5. build-server compiles the source to a Keel bundle, drops the URL into the session’s feed channel
  6. Snack Runtime app inside the emulator picks up the new bundle, loadBundle() swaps the running tree → screen updates → ffmpeg captures → WebRTC stream → browser <video> shows the change
  7. If anyone scans the QR with Keel Go, that device subscribes to the same feed and gets the same bundle

Production deploy

ComponentWhere it lives
Browser frontend (Next.js static export)Aliyun OSS + CDN → snack.keel.appunvs.com
Session orchestrator (Go)Aliyun ECS pool — one orchestrator per region
Android emulator hostsAliyun ECS with KVM + GPU — pool by traffic
iOS Simulator hostsmacOS mac1/mac2 instances — pool by traffic
WebRTC TURN serversSelf-built coturn on cloud edge
build-serverShared with the rest of Keel Cloud Build

See deployment-sop for the Caddy + TLS setup that fronts the orchestrator.

Limits + pricing

  • Free tier sessions: 3-minute video stream cap, then “open in Keel Go” prompt with the QR
  • Pro tier: 30-minute sessions, multi-file projects, npm dep install
  • See pricing

Local dev

The Next.js frontend lives at keel/snack/. From that dir:

npm install
npm run dev   # → http://localhost:3000

Optional — point at a local build-server + session orchestrator:

KEEL_BUILD_ENDPOINT=http://localhost:8082 \
KEEL_SNACK_SESSION_HOST=http://localhost:8090 \
  npm run dev

Single-machine local dev runs an Android emulator via adb; iOS preview defers to a phone via QR + Keel Go (iOS Simulator needs macOS).