Public
Remixed
Sample app for the OpenAI Realtime API
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.

Voice Edit Studio

Voice Edit Studio (talk-to-code) is a voice-controlled workspace for building and editing Val Town vals just by talking. You see a val's live page in an iframe on the left and speak to an AI host (powered by the OpenAI Realtime API) that reads, writes, and creates vals for you in real time.

Live at talk.val.run.

What it does

  • Talk to edit — describe a change ("add a dark mode toggle", "make the header bigger") and the AI edits the val's files, then reloads the preview.
  • Open any val — say "open my X", type a val name in the box (a bare name resolves to your handle; handle/name and pasted val.town URLs work too), click one from Recent vals, or seed it with ?val=handle/name.
  • Create from scratch — the ✨ Create a new val button (or just asking the AI) spins up a minimal HTTP val with a starter page and loads it, so you have an instant canvas to iterate on.
  • Newcomer-friendly — the AI is briefed to assume the person may be brand new to this and to Val Town, orient them in plain language, and offer a concrete first thing to build.
  • Scenes & choreography — vals can ship a CHOREOGRAPHY.md; the AI triggers pre-built overlays via the set_scene tool and can drive slideshows.

How it works

  1. The browser logs in with Val Town OAuth and opens a WebRTC mic connection.
  2. POST /rtc negotiates an OpenAI Realtime session, seeded with the official Townie system prompt plus a Studio-specific addendum, and attaches the signed-in user's Val Town MCP token so the AI can read/write their vals (routes/agent.ts).
  3. The AI calls function tools handled in the browser — load_val, navigate_page, reload_site, set_scene, slide_next/prev/goto — and the val_town_mcp tools for file edits.
  4. The preview iframe reloads on demand (via the reload_site tool or the "↻ Reload site" button) — auto-refresh polling is intentionally disabled.

Running your own / OpenAI key

The deployed studio runs on the owner's OPENAI_API_KEY. If that key is ever rejected or out of quota, the UI shows a "remix this val & add your own OPENAI_API_KEY" prompt instead of a cryptic error.

To run your own copy:

  1. Remix this val (the link in the error, or the Remix button on the val page).
  2. Set OPENAI_API_KEY (an OpenAI key with Realtime API access) in the remixed val's environment variable settings.
  3. Open the val's HTTP endpoint and start talking.

Environment variables

  • OPENAI_API_KEY (required) — OpenAI key with Realtime API access. The app no longer crashes at startup when this is missing; instead /rtc returns a structured error the UI turns into the remix prompt above.

API endpoints

  • GET / — serves the studio (frontend/studio.html)
  • POST /rtc?val=<handle/name> — create a new WebRTC Realtime session (val is optional). Returns { code: "openai" | "upstream", ... } on failure.
  • POST /observer/:callId — internal server-side session control (fire-and-forget)
  • GET /me — current login state
  • GET /auth/token — OAuth token handoff (rarely needed; the studio is same-origin)
  • GET /source — 302-redirects to this val's page on Val Town (used by the remix link, so it's correct even for remixed copies)
  • GET /api/resolve-val?val=<handle/name> — resolve a val to its live HTTP URL
  • GET /api/recent-vals — the signed-in user's most recent vals
  • POST /api/create-val — create a new public val with a minimal HTTP starter

Project structure

├── main.tsx                # Entrypoint: OAuth middleware, route mounting, /me, /source, serves studio
├── frontend/
│   └── studio.html         # The Voice Edit Studio UI (WebRTC, waveform, sidebar, load/create)
└── routes/
    ├── agent.ts            # Realtime session config: tools + Townie prompt + Studio addendum
    ├── rtc.ts              # WebRTC session setup + OpenAI-error classification
    ├── observer.ts         # Server-side session control (WebSocket to OpenAI)
    ├── api.ts              # /api: resolve-val, recent-vals, create-val
    └── utils.ts            # Realtime API URL/header helpers + hasOpenAIKey()

Other files (routes/sip.ts, frontend/index.html, frontend/widget.html, frontend/sdk.js, sdk.ts, oauth-test.tsx) are inherited from the hello-realtime base this was built on and are not mounted by main.tsx.

Notes

  • If the AI says it can't edit files, the Val Town OAuth token has gone stale — log out and back in from the sidebar to refresh it.
  • The preview does not auto-refresh — the AI reloads it after finishing edits, or you can hit "↻ Reload site".