Write a JavaScript file, drop it into a room, and it runs on every client at once over WebSockets. Minigames, drawing boards, quizzes — anything that fits on one disk.
That's the product blurb from the front page. Now let's talk about why any of this exists.
The web is full of services that are tiny on their own, but each one lives off in its own corner. Gartic Phone, Jackbox, skribbl.io, Tally — every little thing gets its own tab, its own site, its own rules. Hence the idea: gather all the little things in one place! And maybe not only the little things.
That's how this platform came about — one that barely limits your imagination, as long as it fits in a single JS file inside a room. And the main thing: every copy of that file is connected over the network. That's where the power is: not running it, but running it together.
But the idea comes with a price attached. If anyone can write the file, then you're running someone else's code on your own machine just because somebody dropped a link. That was the central question of the whole project. So I'll start there, and then go in order: what one disk can actually do, what the shelf looks like, and what I'm not promising to do.
Why it's fine to run someone else's code here
It all rests on one question: what exactly can this code do. So first the list, then what it follows from.
A disk has no network at all: no fetch, no WebSocket, not even an image from a third-party site. No storage — no cookies, no localStorage, nothing that outlives the tab. It can't reach the page around it: it has its own origin, and neither your session nor the address bar is visible to it. The clipboard isn't handed over, for reading or for writing.
What it does get is your nickname, audio, pointer lock and a fullscreen stage — and what goes fullscreen is the platform's frame together with the disk, so it can never occupy the whole screen.
There are no permissions here at all: no consent screen, no checkboxes, no field in the protocol. The set is always the same, so it isn't chosen — it's stated: when a room is created, and again in the lobby, so that whoever joined with someone else's code sees it too.
Now the mechanics. A disk lives in an <iframe sandbox="allow-scripts"> on a separate origin, and the frame's document is served with connect-src 'none'. Three things carry all the weight here.
connect-src 'none'— that's what shuts off the network. Thesandboxattribute doesn't block the network: a frame with an opaque origin will happilyfetchanywhere CORS allows.allow-scriptswithoutallow-same-origin. Together those two flags let the sandbox lift itself. I never have them together.- A separate origin. Site isolation gives the frame its own process, so a disk stuck in a loop hangs itself rather than the tab along with the “Kill script” button.
There's no static code analysis here, and there won't be. A promise in the interface has to rest on a mechanism, not on someone having looked the file over by eye.
There's a disk written specifically to break out: it works through a couple of dozen ways — to reach the network, the storage, the clipboard, the surrounding page, to strip its own sandbox. It runs in a real browser on every build, and not one of them works. A second disk does the opposite: it joins a room and shows what it actually got.
The platform prints the frame policy itself — the studio has a panel showing it exactly as the server sends it and the browser receives it.
What one disk can do
I call the file a disk, though in spirit it's closer to a Dendy cartridge: slot it in and play, nothing to install. One .js, with a manifest in a comment at the top: name, author, version, player count, description, tags. That's it. No package.json, no build step, no dependencies — the platform takes the file as is.
Inside the file you get exactly one object. Here it is:
room.me // { id, nick, avatar } — your real nickname
room.players // who's in the room right now
room.isHost // a fact, not a role
room.send(payload) // to everyone else
room.send(payload, { to: playerId }) // to a single participant
room.on('message', (from, payload) => {})
room.on('join', (player) => {})
room.on('leave', (player) => {})
room.on('hostchange', (player) => {})
That's the entire API.
No ticks, no state, no synchronization. The game loop, who's in charge, what to do when things diverge, how to draw any of it — that's the disk's own business. The platform delivers bytes and provides a box you can't climb out of.
But that goes a long way. Pong, “Chapayev” flick-checkers on a physics engine, a drawing board, a quiz — everything in the library is written with these six lines.
What I promise about delivery
Three rules.
- Per-sender order is preserved. Whatever you sent first arrives first.
- The room's order is the same for everyone. The room accepts messages one at a time, and everyone who received them received them in the same order. But which of two simultaneous messages gets accepted first — that you can't guess.
- At-most-once delivery. The connection drops — the messages are lost, and reconnecting doesn't replay history. That's deliberate.
Limits sit on the connection, not on the room — that way a noisy participant slows down themselves rather than everyone: 4 KiB per message, 60 sends per second, 8 people per room. Going over doesn't tear down the connection: the message is dropped, a notification arrives, the connection lives on.
What the shelf looks like
Every disk has a permanent address — /disk/<hash>. The hash is the identity: edit the file and you get different bytes, a different hash and a different entry. Swapping out the content behind someone else's link is impossible in principle, because the link names bytes, not a name.
There are no accounts here at all. A session is issued on your first visit, and the only thing you can change in it is the nickname. The author name in the manifest isn't backed by anything: nothing in the system depends on it.
You can poke at a disk without inviting anyone
You don't have to take someone else's disk on faith, and you don't have to gather people in a room for it before you even know what it turns out to be.
Every disk page has Run it here. The very same bytes run right there, in the very same frame the room mounts them into. Nothing starts by itself — you start it.
You can go solo — then the disk is told the truth about where it is: there's no room, room.me is null. Or you can go two-seat, and that's already the same Multiplayer lab that lives in the studio: up to four local seats that join, leave and restart, with latency of your choice — instant, 60, 180 or 500 ms. That's how you check the roster, direct messages, late joins and host handoff — before publishing.
The studio and agents
The manifest is read by the same code on both sides: the server on upload, the page while you type. Duplicating those rules on one side isn't an option, or you'll run into a rejection where the field says “all good”.
A disk is a perfect task for an agent: everything a disk is allowed to do fits in a single file. That's why the rules are published as a single file — diskroom.app/skill.md: the manifest grammar, the whole room API, the frame policy, the delivery guarantees and a fully working disk at the end. The Prompt for an agent button hands you ready-made text: the guide's address and an instruction to save it as a skill. Nothing gets installed and nothing leaves the page — the disk comes back as text and goes straight into the same editor.
A live example: the conversation with an agent that produced this disk. It sits in the library and runs right on its own page.
What I'm not promising
There are things diskroom doesn't do:
- No CPU or memory limits. The browser doesn't give you those knobs. The ceiling is a heartbeat and a “Kill script” button; a memory bomb will still take the tab down.
- No anti-cheat. The disk executes in the player's browser, and the player controls that browser completely. Host authority means trusting the host, like in the old P2P games.
- There's no protection against a phishing interface drawn by the disk itself — other than the fact that the frame always sits inside the platform's visible chrome and never takes over the whole screen.
What's inside
The backend is Rust: axum holds HTTP and WebSockets, and it also serves the frame document along with its policy. The wire types sit on postcard, and all the limits are collected there too — in one place, so they don't drift apart between the server and the page. The frontend is Leptos compiled to wasm; it's the one that owns the WebSocket, because the socket belongs to the platform, not to the disk. Storage is Postgres.
The repository is closed for now, so all I can show is the result.
Claude and Codex were an enormous help — without them this would have taken far longer.
Try it
You can come in without registering — there simply isn't any. Open diskroom.app, poke any disk in the library, hit Run it here — and that's it, no one to call over.
And if you want one of your own — one file and a six-line header.
I'd love to hear what you think! Message me on Telegram: @Tikitko