Real-time for the apps your AI builds
Pages that change for everyone on them within a second of something happening, like a new chat line or another player's move.
Say you want a scoreboard for Sunday's pickup game. Ask ChatGPT or Claude for one, and when you type in a score from your phone, every phone and TV with the page open shows it within a second.
Built with it
Bootleg City
A multiplayer game where friends steal cars and outrun the cops in a 1920s city. It was built by describing it to Claude, over a few days of playtesting. The game is one HTML file, and there's no game server behind it.
Play Bootleg CitySquigly
A whiteboard that anyone with the link can draw on at the same time, with each person's cursor and name on the board. Claude built it in an afternoon. Drawings are encrypted in the browser, so Tessryx relays and saves every change without being able to read it.
Open SquiglyTry it
A live page, shared with everyone reading this one
Not connected
wss://someone.tessryx.app/_live/demo/live
Reactions from everyone here float up in this space.
One reaction every 0.3 seconds, checked at the edge
If you build with ChatGPT or Claude
Ask for a page that updates live
Your AI sets up the live page and whatever sends it updates, and it adds checks to anything people send.
A live scoreboard “make a scoreboard page for our pickup basketball games. i want to change the score from my phone and have it update right away on the tv at the gym”
A chat room with who's online “build a chat room for my book club with a list of who's online. only people i invite should be able to get in”
A two-player game “make a two-player tic-tac-toe game i can send a friend as a link. moves should show up for both of us instantly and nobody can move out of turn”
If you're a developer
A real-time backend for an app you host anywhere
Your app can live on any host. It connects to a live endpoint on Tessryx, and you bring your own users: your backend signs a short token, and a join workflow checks it with a key you stored as a secret. The workflow decides who gets in, the id they appear under, which channels they may send on and when their access ends.
Browsers don't apply CORS to WebSockets, and a live endpoint has no CORS setting.
Who gets in: a page on the Tessryx site itself can use the endpoint's own sign-in gate. A page on another website doesn't carry that sign-in, so there the join workflow and your tokens decide. A live endpoint with neither is open to anyone.
{
"steps": [
{
"name": "check",
"blocks": ["verify", "variables"],
"verify": {
"mode": "jwt",
"secret": "game-jwt-key",
"token": { "language": "jsonata", "source": "$input.token" },
"audience": { "language": "jsonata", "source": "'my-game'" }
},
"variables": [
{ "name": "v", "value": { "language": "jsonata", "source": "$verify" } }
]
}
],
"final_transform": {
"language": "jsonata",
"source": "$v.ok and $v.claims.room = $input.room ? { 'admit': true, 'viewer_id': $v.claims.sub, 'can_send': $v.claims.send, 'stream': { 'room': $input.room }, 'exp': $v.claims.exp * 1000, 'profile': { 'name': $v.claims.name } } : { 'admit': false, 'reason': 'Your link has expired' }"
},
"output_handling": { "disposition": "display_only", "content_type": "application/json" }
}{
"workflow": "game/shape@published",
"input_mode": "transform",
"query": ["room", "token"],
"input_transform": "{ 'room': query.room, 'token': query.token }",
"live": {
"stream": "game",
"join": { "workflow": "game/join@published" },
"presence": true,
"channels": {
"moves": { "mode": "relay", "schema": "game/move@published", "keep": "latest" }
}
}
}<script src="https://acme.tessryx.app/_tessryx/site-1.js"></script>
<script>
const live = tessryx.live('game/live', {
host: 'acme.tessryx.app',
query: { room: 'r1', token: tokenFromYourBackend },
onRelay: (channel, entries) => draw(entries),
onPresence: (list, joined, left, count) => showPlayers(list, count),
onStatus: (status, reason) => status === 'closed' && rejoinWithAFreshToken(),
});
live.send('moves', { x: 120, y: 48 });
</script>How it works
- 01
Sending an update
A publish step in any workflow, like a webhook route or a schedule, sends an event to a stream. The live endpoint's workflow turns it into what viewers see, once for each different thing they're watching, like each match on a scores page, and every open page gets the result.
- 02
Players and chat
A relay channel passes each sender's messages straight to everyone else, in batches about ten times a second, with no run per message: positions in a game, strokes on a whiteboard. A screened channel sends each message through one of your routes first, which suits chat.
- 03
Checks on every message
At no charge, the edge holds each relay message to the channel's schema and its sender's rate. Rules can add a cooldown between messages, numbers that may only go up, like a score, and fields kept for each sender.
- 04
Game rules
For a top speed, a weapon's cooldown, ammo or whose turn it is, a handler workflow checks each batch before anyone sees it. It can drop a message, rewrite it, change the shared state or pass it on to another workflow.
- 05
Who's here
Pages get the list of people on them, who joined and who left, and a count. A reload or a dropped connection inside 30 seconds doesn't count as leaving.
- 06
Shared state for a room
Round settings or a game board can live in the room, which every page gets when it joins and whenever it changes. Workflows can fill it from your records when a stream starts, and write it back every few minutes.
- 07
Leaving, and coming back
A leave workflow can run once for each person who's gone for good, say to give their seat to the next player. A connection that drops reconnects by itself and catches up on what it missed, free within a few minutes.
Browser
Your page, on Tessryx or any other host
Edge
Connections, checks, relay and presence
Your workflows
Updates, joins, game rules and saves
What it costs
Real-time uses the same credits as everything else. Free comes with 10,000 a month and Pro with 100,000.
| What | Credits |
|---|---|
| Someone joining | 1 credit. Reconnecting within a few minutes is free. |
| An update | 1 credit, or 1 for each different thing viewers are watching, like separate matches. |
| Relaying | 1 credit for each person who sends in a minute, and at least 2 a minute while anyone sends. |
| A handler | 8 credits for each minute of relaying it checks. |
| Big audiences | 2 credits per relaying minute for each thousand watchers past the first thousand. |
| Who's here | Free. |
| Join, first view, leave and room workflows, and each chat line | 1 credit a run, like any run. |
- 4 players joining
- 4
- 4 senders, 60 minutes of relaying
- 240
- The handler, 60 minutes
- 480
- Total
- 724 credits
About 138 hours of play on Pro, before any saves or chat lines.
- 1,000 people joining
- 1,000
- One update a minute, 120 minutes
- 120
- Total
- 1,120 credits
Viewers who only watch cost their join and nothing more. More than 100 people needs Pro or above.
Limits
- Viewers per live page
- 100 people at once on Free, and 5,000 on Pro, Agency and Scale. Once a page is full, the next person to arrive gets a message saying so, and everyone already on it stays connected.
- Ordinary pages aren't streamed
- Updates reach a page over its live connection. A normal page or route still answers in one piece, with no server-sent events or streamed responses.
- A handler does calculation only
- It can use variables, conditions, asserts, loops and a final answer, in JavaScript. It can't read or write records or call another service, so it forwards a message to a workflow for that.
- The list of who is here stops at 100
- Past 100 people, pages get the count without the list.
Questions
Do I need a server?
Not for a page on Tessryx: the connections and the checks on every message run at Tessryx's edge, and your logic runs as workflows. An app hosted somewhere else needs a way to sign tokens, and only if it brings its own users.
Can I use my own login?
Your backend signs a short token, a JWT or an HMAC signature, with a key you also store as a Tessryx secret. A join workflow checks it and admits the person under your own id for them, with the channels you allow and a time their access ends. A page on the Tessryx site itself can use sign-in with Tessryx instead.
What happens when credits run out?
New joins are refused and relaying pauses, with the open connections kept, and pages are told live updates are paused. A top-up within 10 minutes picks up where it left off. After that the pages close with the same reason and try again every 10 minutes.
How many people can be on one page?
From 100 on the free plan to 5,000 on Pro and above. Past the first thousand watchers, a page where people are sending costs 2 more credits a minute for each extra thousand.
Does it work on phones?
It's a page in the browser like any other. A phone that puts the tab away loses the connection, and the page picks it up again when the tab comes back, without another join charge if that's within a few minutes.
Does it cost anything when nobody is watching?
An update only runs when someone is connected to see it.
Under the hood
The technical details
- live.stream
- The stream the page listens to. Any workflow sends to it with a publish step: a webhook route, a schedule, a form post.
- shaping
- The live endpoint's own workflow turns each event into what viewers get ($event, $input), once per event for each different input among the people watching. A thousand viewers of one match is one run, and nobody watching means no run. Returning null sends nothing.
- live.initial
- Optional. A workflow that renders what someone sees the moment they join, so they don't wait for the next update.
- tessryx.live()
- The page connects with the site runtime: onEvent for updates, onStatus for reconnecting, paused, full or closed, each with a reason written for the visitor. A page on another website names the host it connects to.
- reconnects
- A dropped connection reconnects by itself and is sent what it missed, up to a minute's worth. Back within a few minutes, it's free.
- viewer cap
- People on one live endpoint at once: Free 100; Pro, Agency and Scale 5,000. Past it a new viewer is told the page is full, and nobody watching is dropped.
- credits
- An update is a credit for each different input, however many watch. A viewer joining is a credit, like a page view.
{
"workflow": "scores/shape@published",
"input_mode": "transform",
"query": ["match"],
"input_transform": "{ 'match': query.match }",
"live": {
"stream": "scores",
"initial": { "workflow": "scores/shape@published" }
}
}Related features
Channels for players and chat
People on a live page can send too. Positions in a game or strokes on a whiteboard pass straight to everyone else once their shape is checked, and chat lines go through your own logic first.
Game rules checked at the edge
Your own rules for what players send, like a top speed, a weapon's cooldown, ammo or whose turn it is. Every message is checked against them before anyone else sees it, and the ones that break them are dropped.
Your own users on live pages
Decide who gets onto a live page and who they are. Your own backend signs a short token and Tessryx checks it with a key you stored. The person joins under your id for them, with the channels you allow.
Who's here (presence)
A live page can show who is on it, who just joined and who left, and how many are there, with each person's name when you have one. Showing it uses no credits, and a quick reload doesn't show anyone leaving.
Build it
Multiplayer game
Players in the same world at once, with rules nobody can skip
Live chat
A chat room with who's online, where every line passes your rules first
Collaborative whiteboard
One board that everyone with the link draws on at the same time
WebSocket backend
Live updates and who's online for an app you host anywhere, with your own users
League or club site
A league site whose standings update live on game night
API dashboard
Numbers from the services you use, kept fresh on a schedule