walla-page
v1.0.3
Published
Put HTML on a TV from the command line.
Maintainers
Readme
Walla Page
Put HTML on a TV from the command line.
Walla Page is a CLI for you or your agent to control a screen. It shows fullscreen HTML scenes in a browser display or on a Chromecast, can schedule changes ahead of time, and can speak over the display with text-to-speech.
Quickstart
Install:
npm install -g walla-pageThe published CLI points at https://walla.page by default. If you want to run your own deployment, keep reading and set your own BASE_URL, Cast app ID, and ElevenLabs key.
Create a room:
walla createOpen the control page:
walla cast --openFrom there, cast to a Chromecast if you have one configured, or open the browser fallback display.
Show something on the wall:
walla show ./examples/cast-tests/old-timey-check.htmlSpeak over the wall:
walla say "Dinner in five minutes."Clear the wall:
walla clearCommands
Core flow:
| Command | What it does |
| --- | --- |
| walla create | Create a room and save it as the current room |
| walla cast [--open] | Print or open the control page for the current room. If the saved room is stale, it creates a fresh one automatically |
| walla show <file.html> [--title <title>] [--duration <seconds>] | Show HTML on the wall now. If you omit --duration, it stays up until replaced or cleared. Also aliased as walla push |
| walla say <text> [--at <time>] [--title <title>] [--voice-id <id>] [--max-words <n>] | Generate speech and play it over the current wall. At least one display must be connected |
| walla clear | Clear the current wall without deleting the room |
| walla delete [--force] | Delete the room. With --force, disconnect connected displays too |
Scheduling and inspection:
| Command | What it does |
| --- | --- |
| walla schedule <file.html> --at <time> [--title <title>] [--duration <seconds>] | Schedule HTML for later |
| walla status | Show the current room ID, control URL, browser fallback info, scene state, and socket counts |
Admin:
| Command | What it does |
| --- | --- |
| walla config [--server <url>] | Print local CLI config or point the current room at another backend |
| walla logout | Remove local CLI config |
| walla quickstart | Print a short onboarding flow |
Common Examples
Create a private room (browser fallback requires a display token):
walla create --private-displayCreate a room with a display connection cap:
walla create --display-limit 4Show a lightweight test scene:
walla show ./examples/cast-tests/signal-bars.htmlShow the campfire scene:
walla show ./examples/campfire.htmlSchedule a scene for 10 minutes from now:
walla schedule ./examples/cast-tests/old-timey-check.html --at "+10m"Inspect the current room:
walla statusPublic vs Private Display
Rooms default to a public browser fallback — anyone with the room URL can view it.
- Public room: browser fallback works without a token.
walla statusshows a directbrowserDisplayUrl. - Private room: browser fallback requires a display token. The control page opens the browser fallback for you. Use
walla create --private-display.
Cast Setup (Optional)
Walla Page works without a Chromecast. If you want to cast to a Chromecast from your own deployment, register your own custom Cast receiver application.
- Create a
Custom Receiverapplication in the Google Cast SDK Developer Console. - Set the Receiver Application URL to your deployed receiver page, for example
https://your-wall.example.com/cast/receiver. - Register each Chromecast device for testing if the Cast app is unpublished.
- Set the resulting app ID as
CAST_APP_IDin the Worker environment.
See the Cast registration docs for details.
Running Your Own Copy
You do not need to deploy this yourself to use the published walla CLI. It talks to https://walla.page by default.
If you want your own version, this repo is the starting point. You can fork it, run it on your own Cloudflare account and domain, wire up your own Cast app ID and ElevenLabs key, and extend it however you want.
Local Development
npm install
cp .dev.vars.example .dev.vars
npm run devExample .dev.vars:
BASE_URL=http://127.0.0.1:8787
CAST_APP_ID=
ELEVENLABS_API_KEY=
ELEVENLABS_VOICE_ID=Everything works without CAST_APP_ID and ELEVENLABS_API_KEY — you just won't have Cast or text-to-speech until you set them up.
Deploy
npm run deployAfter deploy, set your own environment values in Cloudflare. The published walla CLI can talk to walla.page out of the box, but your own deployment should use its own BASE_URL, Cast app ID, and ElevenLabs setup.
Set CAST_APP_ID as a normal Worker variable. Set ELEVENLABS_API_KEY as a Wrangler secret:
npx wrangler secret put ELEVENLABS_API_KEYEnvironment
These settings are for your own deployment.
Required for a real deployment:
| Variable | Description |
| --- | --- |
| BASE_URL | Canonical origin for your deployed worker, such as https://your-wall.example.com |
Optional feature config:
| Variable | Description | If unset |
| --- | --- | --- |
| CAST_APP_ID | Your Google Cast application ID for Chromecast support | Cast stays disabled |
| ELEVENLABS_API_KEY | Your ElevenLabs API key for walla say | TTS stays disabled |
| ELEVENLABS_VOICE_ID | Default ElevenLabs voice ID | Uses the built-in default voice ID |
Runtime tuning:
| Variable | Description | Code fallback |
| --- | --- | --- |
| ROOM_IDLE_TTL_MS | How long an idle room lives before cleanup | 7 days |
| CREATE_ROOM_LIMIT | Max rooms per IP in the rate limit window | 5 |
| CREATE_ROOM_WINDOW_MS | Rate limit window for room creation | 10 minutes |
| TTS_LIMIT | Max TTS requests per IP in the rate limit window | 3 |
| TTS_WINDOW_MS | Rate limit window for TTS | 1 hour |
| TTS_MAX_WORDS | Max words per TTS request | 60 |
| DISPLAY_TOKEN_TTL_MS | How long a display viewer token stays valid | 15 minutes |
| PRODUCER_TOKEN_TTL_MS | How long a room controller token stays valid | 7 days |
HTTP API
These are the underlying endpoints the CLI uses. If you want to integrate directly, open an issue.
Room lifecycle:
POST /api/roomsDELETE /api/rooms/:roomId
Room control:
GET /api/rooms/:roomId/statePOST /api/rooms/:roomId/pairPOST /api/rooms/:roomId/schedulePOST /api/rooms/:roomId/ttsPOST /api/rooms/:roomId/speakPOST /api/rooms/:roomId/clearPOST /api/rooms/:roomId/showcaseGET /api/rooms/:roomId/ws
Pages:
GET /rooms/:roomId— browser fallback displayGET /rooms/:roomId/control— control pageGET /cast/receiver— Cast receiver
Testing
npm run check
npm run test:smoke