Public
Reactor SANA-Streaming real-time video editor on Val Town
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data โ€“ all from the browser, and deployed in milliseconds.

SANA-Streaming on Val Town

A Val Town port of Reactor's SANA-Streaming example โ€” their real-time video editing model. Point your webcam at it, type an edit instruction, and watch yourself transformed frame-by-frame. Re-prompt mid-stream and the new edit lands at the next chunk boundary (~1s).

The original is a Next.js 15 + React 19 app. This port keeps the same architecture but runs on Val Town's Deno runtime: a Hono server serves a client-side React SPA and mints the Reactor JWT; the browser connects directly to api.reactor.inc over WebRTC, so Val Town never proxies realtime media.

Architecture

Rendering mermaid diagram...

The REACTOR_API_KEY stays on the server. The browser only ever receives a short-lived JWT, cached by its HTTP cache until expiry.

Files

PathWhat it is
index.tsHono server: serves the SPA, mints the JWT (/api/reactor/token), /source redirect
frontend/index.htmlSPA shell โ€” Twind (runtime Tailwind) + the React entry
frontend/index.tsxReact root
frontend/App.tsxProvider + connection, seed, start/pause/resume/reset, live re-prompt, webcam self-view, edited output

Setup

One env var, the Reactor production key (rk_...):

๐Ÿ‘‰ Add REACTOR_API_KEY here: https://www.val.town/x/dcm31/sana-streaming/environment-variables?key=REACTOR_API_KEY

How to use

  1. Open the app, click Connect (disconnected โ†’ connecting โ†’ waiting โ†’ ready).
  2. Allow camera access when prompted.
  3. (Optional) set a seed for reproducibility, then Start.
  4. Type an edit prompt (or tap a preset) โ€” it's an editing instruction, not a scene description. Change it any time; it applies ~1 chunk later.
  5. Pause / Resume / Reset once running.

What's faithful vs. simplified

Faithful to the reference app: the server-side JWT mint (browser never sees the key), the model-as-source-of-truth reducer fed only by the typed state message, status-gated commands, the set_mode("live") โ†’ start flow, mid-stream re-prompting, and the typed-SDK surface (useSanaStreaming, useSanaStreamingState, SanaStreamingMainVideoView).

Simplified for a single-file Val Town SPA:

  • Webcam only. Uses the declarative <SanaStreamingCameraView>, which auto-acquires the webcam and auto-publishes the camera track. The reference app's second input source (streaming a video clip into camera via captureStream(), with the split source/edited stage) and its manual contentHint = "detail" publish are not ported.
  • No Snap-Clip recording (the reference's @reactor-team/js-sdk clip recorder).
  • Tailwind via Twind at runtime instead of Tailwind v4 + @reactor-team/ui design tokens.

All three are straightforward to add on top โ€” the SDK exports the same hooks the reference uses.

Notes

  • React is pinned to 18.2.0 everywhere (the SDK's peer dep is react >=18) so the browser loads exactly one React instance from esm.sh.
  • Val Town can't accept incoming WebSockets, but that's fine here โ€” the browser's realtime connection is to Reactor, not to Val Town.