---
name: playtest
description: >-
  Publish a playable web build (game, demo, interactive sketch) to a link real people can open
  with no account and no install, get a QR code and a forwardable invite card, and see who played.
  Use when the user has a built web game or interactive demo and wants people to try it, or has a
  dev server running and wants someone else to open it. Do not use for plain static hosting of
  reports, dashboards or docs sites.
---

# playtest

## Install

Single binary, no runtime. macOS / Linux:

```
curl -fsSL https://playtest.run/install.sh | bash
```

Installs to `~/.local/bin/playtest` after a SHA-256 check. Windows: download the zip from
https://github.com/roviix/playtest.run/releases/latest and put `playtest.exe` on PATH.
Verify with `playtest --version`.

Prefer the MCP server if you can run it — one call gives you the link, the QR text and the
invite card image:

```
playtest mcp
```

## Decide which path first

| The user has | Do this |
| --- | --- |
| A built directory (`dist/`, `build/web/`, a Godot/Unity web export) | `playtest ./dist` |
| A dev server already running on a port | `playtest 5173` |
| A built frontend **and** a backend they run locally | `playtest ./dist --backend 3000` |
| Only source, no build | Run their build first. Do not upload source. |

Always pass `--json` when you are going to parse the answer. stdout is exactly one JSON object;
all narration goes to stderr.

## Publishing

```
playtest ./dist --json
```

Answer fields worth reading: `url`, `qr_text`, `card_path`, `expires_at`, `console_url`,
`findings`, `elapsed_ms`.

Useful flags:

- `-n, --name` the title players see before they start
- `-m, --note` what changed in this version — shown on the invitation page, sent to followers, and
  used as the "what I want you to look at" line on the Plaza
- `--summary` the persistent project description; omitted values keep the existing description
- `--cover cover.png` the first thing players see; without it we typeset a plain title card
- `--public` put it on the Plaza; `--seats 10` also marks it as looking for that many testers
- `--card <path>` explicitly download an invite card (default: no download; `-` also skips it)
- `--isolated=on|off|auto` cross-origin isolation; Godot 4 threaded exports need it (auto detects)

## After publishing, hand the user three things

1. The **url**.
2. The **QR code** — paste `qr_text` verbatim inside a code block. It only scans in a monospace
   block; do not reflow or summarise it.
3. If an image is needed, run `playtest card --out invite.png` or use the MCP image block. CLI uploads do not write a card by default; `card_path` appears only after an explicit successful download. This matters more than it sounds: in most chat apps a bare
   link shows no title and no face, and a picture with the developer's name on it is what people
   actually forward. Attach the image; do not just say it exists.

## Reading failures

Exit codes: 0 ok, 2 usage, 3 needs login, 4 network, 5 server, 6 bad input, 7 quota.

Exit 6 usually means we inspected the build and know it will not open — a missing top-level
`index.html`, a threaded export without isolation, a missing `.pck`/`.data`. `message` and `hint`
say what to change. Fix the build; retrying the same bytes will fail the same way.

## What to tell the user honestly

- Without logging in, the link expires in 24 hours. `playtest login` (email or GitHub) keeps it, and
  adopts links they already made anonymously.
- We never run their code and never rewrite their bytes.
- Players never need an account. First play is zero-friction by design.
- This is not general static hosting; the root domain is the Plaza, for projects looking for testers.

## Other commands

```
playtest ls                 # projects on this machine (with follower counts)
playtest versions <slug>    # every version, and which one players see
playtest rollback <slug> 3  # point players back at v3; no bytes re-uploaded
playtest card <slug>        # fetch the current invite card again
playtest files <slug>       # what is actually live: every path, size and sha256
playtest unlist <slug>      # take it off the Plaza; the link keeps working
playtest open <slug>
playtest rm <slug> -y
```

## HTTP API

If you cannot run the binary: https://playtest.run/llms-full.txt and https://playtest.run/openapi.json.
Publishing is `POST /v1/projects` -> `POST /v1/projects/{slug}/uploads` -> `PUT /v1/blobs/{hash}`
-> `POST /v1/projects/{slug}/uploads/{id}/commit`.
