# The Internet's Game Console
Arcadible is a platform for publishing games. Bring your own agent, build with the tools you already use, and turn your game into a release, a store page, and a playable link with a single command. Play your game anywhere Arcadible is installed, or in any browser.
Arcadible isn't an engine or a framework. It's closer to `npm publish` for games: if your game builds to a folder with an `index.html`, it ships. Build with Three.js, Phaser, PixiJS, Babylon.js, the web export of your engine, or your own code. The optional [Arcadible SDK](https://developer.arcadible.com/build/saves) adds saves that carry over from one version to the next.
```sh
npm install -g arcadible
arc login
arc init star-drift && cd star-drift
arc deploy
arc play
```
That's a starter game, published and playable.
## Choose your path
- **[Start a new game](https://developer.arcadible.com/getting-started):** from an empty directory to a public game, step by step.
- **[Bring an existing game](https://developer.arcadible.com/existing-game):** add a manifest to a game that already builds, and tell Arcadible how to build it.
- **[Release from GitHub](https://developer.arcadible.com/publish/deploying-from-github):** let GitHub Actions build each release from a tag.
- **[Work with an AI agent](https://developer.arcadible.com/build/ai-agents):** give your coding agent these docs and an `AGENTS.md`.
## How it fits together
The [Arcadible CLI](https://developer.arcadible.com/references/arcadible-cli) checks your project, builds it, and deploys it. Arcadible publishes each version as a build, and the store shows its store page, made from your README, your art in `.arcadible/`, and your `arcadible.toml`. [Arcadible Studio](https://developer.arcadible.com/studio) shows your games and their releases. [How Arcadible Works](https://developer.arcadible.com/concepts) defines each part.
When something goes wrong, start with [Troubleshooting](https://developer.arcadible.com/troubleshooting), or look up a message in [Errors](https://developer.arcadible.com/references/errors).
## For AI agents
These docs are also published as text: [llms.txt](https://developer.arcadible.com/llms.txt), [llms-full.txt](https://developer.arcadible.com/llms-full.txt), and a `.md` for every page. See [Working with AI Agents](https://developer.arcadible.com/build/ai-agents).
# Getting Started with Arcadible Game Development
This guide takes a new game from an empty directory to a public game on Arcadible, using a starter template. You deploy to staging first, make the store page your own, then release it to players. To publish a game you've already built, see [Bring an Existing Game](https://developer.arcadible.com/existing-game).
## Before you begin
You need:
- **An Arcadible account.** Join at [arcadible.com](https://arcadible.com).
- **Node.js 24 or later**, to run the CLI. Your game doesn't need to use Node.js.
## Install the CLI
The CLI is published to npm as `arcadible`, and its command is `arc`:
```sh
npm install -g arcadible
arc --version
```
## Log in
```sh
arc login
```
[`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) prints a link and a code. Open the link, log in, and enter the code, or scan the QR code with a phone that's already logged in. No password is typed into the terminal. Check which account you're using with:
```sh
arc whoami
```
See [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) for headless machines and where the session is stored.
## Create a project
```sh
arc init star-drift
cd star-drift
```
[`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) asks for a template: a blank canvas, the default, or a [starter](https://developer.arcadible.com/build/game-engines#starter-templates) for Three.js, Phaser, PixiJS, or Babylon.js. Name one with `-t` to skip the question:
```sh
arc init star-drift -t arcadible/starters/phaser
```
Any public GitHub repository, or a folder in one, works as a template too: `arc init star-drift -t user/repo`.
## Project structure
The blank starter looks like this:
```text
star-drift/
├── arcadible.toml # the manifest
├── README.md # the store page
├── AGENTS.md # instructions for coding agents
├── .arcadible/ # the store art
│ ├── icon.png, cover.png, banner.png, poster.png, splash.png
│ └── gallery/ # screenshots
├── .arcadible-public/ # the game itself
│ ├── index.html
│ └── main.js
└── .github/workflows/
└── arcadible-release.yml # used by arc deploy git
```
- **`arcadible.toml`** holds what can't be derived from anywhere else. [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) has already given it a unique `id`; commit it and never change it.
- **`README.md`** is your store page: its headings and paragraphs become the title, tagline, bio, and description.
- **`.arcadible/`** holds the art. Each file has a fixed name, so there's nothing to reference. The starter's placeholders are drawn at the recommended sizes, with each piece's [edge margin](https://developer.arcadible.com/publish/store-page#edge-margin) marked, and `gallery/` has three screenshots, each with a `_phone` one.
- **`.arcadible-public/`** is what players load. The blank starter has no build step, so this is the game's source. The engine starters build into it with Vite, from `index.html` and `main.js` at the root.
To play the game while you work on it, run [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) and open the link it prints: a local play page that runs the game as Arcadible does, and reloads it as you edit. See [Testing Your Game](https://developer.arcadible.com/build/testing#locally).
## Deploy to staging
Your first deploy goes to staging: playable by link, but not shown to players. Until the game's first production release, its store page says only "Not released yet". The starters begin at a prerelease version, which is what sends a build to staging. In the blank starter, it's in `arcadible.toml`:
```toml title="arcadible.toml"
[game]
version = "0.1.0-beta.1"
```
The engine starters have a `package.json`, so it's `"version"` there instead. Deploy, and play it:
```sh
arc deploy
arc play
```
[`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) validates the project, runs your build, packages the game with its store art, and starts a release. It waits until Arcadible has published the build, then prints where to play it, or says why the release failed. See [Releases](https://developer.arcadible.com/publish/releases).
[`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) opens the game in the Arcadible desktop app, or a browser if the app isn't installed. Until the game has a production release, it opens staging. Each version publishes once, so bump the prerelease number, such as `0.1.0-beta.2`, before each deploy. See [Channels](https://developer.arcadible.com/concepts#channels).
## Make it yours
Give the game its own name, handle, and art. None of it is required to deploy, so do it in any order, deploying to staging as you go. To see the store page each deploy would publish, open the game in [Arcadible Studio](https://developer.arcadible.com/studio#store), then its **Store** tab, under **Staging**.
1. **Name it.** Edit the headings and the bio in `README.md`:
```md title="README.md"
# Star Drift
## Drift. Dodge. Survive.
A one-thumb arcade game about surfing a dying star.
---
Pilot a freighter through **collapsing orbits**.
---
```
The first heading is the title, the `##` heading after it the tagline, the paragraph after that the bio, and everything between the first and last `---` the description. See [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown).
2. **Claim a handle.** The handle is the name in your game's links, such as `play.arcadible.com/star-drift/`. It's unique among games, and a game keeps it for 90 days until its first production release ([Limits › Handles](https://developer.arcadible.com/references/limits#handles)):
```toml title="arcadible.toml"
[game]
handle = "star-drift"
genres = ["arcade", "action"]
```
Pick up to three [genres](https://developer.arcadible.com/references/arcadible-manifest#game.genres) while you're there, and check the [inputs](https://developer.arcadible.com/references/arcadible-manifest#game.inputs) and [orientation](https://developer.arcadible.com/references/arcadible-manifest#game.orientation) your game supports. Check that [`audience`](https://developer.arcadible.com/references/arcadible-manifest#game.audience) and [`ai`](https://developer.arcadible.com/references/arcadible-manifest#game.ai) describe your game: the starter sets them to `everyone` and `unused`.
3. **Replace the art.** Swap the images in `.arcadible/` and `.arcadible/gallery/` for your own, keeping the file names and aspect ratios, and keeping what matters inside the placeholders' [edge margin](https://developer.arcadible.com/publish/store-page#edge-margin). See [Your Store Page](https://developer.arcadible.com/publish/store-page).
Then check the project:
```sh
arc validate # the same checks arc deploy runs
arc inspect # the manifest as Arcadible sees it
```
[`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) lists every problem with a link to the rule it breaks. [`arc inspect`](https://developer.arcadible.com/references/arcadible-cli/arc-inspect) shows where each value ended up, such as `arc inspect game.title`.
## Release it
When the game is ready, release it to production with a version without a suffix:
```toml title="arcadible.toml"
[game]
version = "0.1.0"
```
```sh
arc deploy
arc open # open its store page
```
A production release becomes what players get, and builds the store page from your README, art, and manifest. Your game is **unlisted** to start with: anyone with the link can play it, but it doesn't appear in the store or in search. Share the link with testers at this stage:
```text
https://play.arcadible.com/star-drift/
```
To ship an update, change the game, bump the version, and deploy again. Try it on staging first with a prerelease, such as `0.2.0-beta.1`, then release `0.2.0`.
## Go public
When you're ready for players to find it, set the visibility and deploy a release version:
```toml title="arcadible.toml"
[game]
visibility = "public"
```
Your game now appears in the Arcadible store and in search. [Going Public](https://developer.arcadible.com/publish/going-public) has a checklist for before you do.
## Release from GitHub
Instead of deploying from your machine, GitHub Actions can build each release from a tag: [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git). The starters include the workflow it needs. See [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github) to set it up.
## Next steps
- [How Arcadible Works](https://developer.arcadible.com/concepts): every term these docs use.
- [Testing Your Game](https://developer.arcadible.com/build/testing): locally, on staging, and on devices.
- [Best Practices](https://developer.arcadible.com/build/best-practices): loading, performance, input, and lifecycle for games on the web.
- [Runtime Limitations](https://developer.arcadible.com/build/runtime-limitations): what the sandbox allows and blocks.
- [Arcadible Studio](https://developer.arcadible.com/studio): your games, their builds, and releases.
# Bring an Existing Game to Arcadible
This guide publishes a game you've already built. Arcadible needs two things from it: a folder with an `index.html` that runs the game, and an `arcadible.toml`. Your code, engine, and build stay as they are. For a new game, start with [Getting Started](https://developer.arcadible.com/getting-started) instead.
## Add a manifest
Install the CLI and log in, as in [Getting Started](https://developer.arcadible.com/getting-started#install-the-cli), then run [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) in the game's directory:
```sh
cd my-game
arc init
```
In a directory that exists, `arc init` doesn't copy a template. It creates `arcadible.toml` if there isn't one, and gives it a game [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id). It also adds a [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version), unless your `package.json` has one, which Arcadible uses instead. Commit `arcadible.toml`, and never change its `id`.
## Tell Arcadible how to build it
Tell Arcadible the folder your build writes, and, if `package.json` doesn't build it, the command that does. A Vite project needs only the folder, since its `build` script runs as `npm run build`, and relative paths:
```toml title="arcadible.toml"
[workflow]
deploy_dir = "dist" # contains index.html
```
```js title="vite.config.js"
export default { base: "./" }; // relative paths
```
- **[`build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build)** runs before every deploy, and before [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve). Without it, a project with a `build` script in `package.json` builds with `npm run build`. Set it for anything else, such as `build = "./build.sh"`, leave it out if your game has no build step, or set `build = ""` to skip `package.json`'s.
- **[`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir)** is the folder with your `index.html`, which players load. It defaults to `.arcadible-public`.
For an engine's web export, such as Godot's, see [Game Engines](https://developer.arcadible.com/build/game-engines). Then check the result:
```sh
arc build # runs the build, and says if deploy_dir is missing
arc validate # checks everything arc deploy will
arc serve # plays the build locally, as Arcadible runs it
```
## Describe it
Your store page comes from your project's files, so most of it can come from what you already have:
- **`README.md`:** its `#` heading becomes the title, and a `##` heading right after it becomes the tagline, so a README that starts `# My Game` then `## Installation` gets "Installation" as its tagline, and fails [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) until its description is between two `---` rules. Either shape the README for the store (title, `##` tagline, one-line bio, then the description between two `---` rules; anything after the last rule isn't shown), or leave it as it is and put the store page in an `arcadible.md`, which Arcadible reads instead. See [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown).
- **`.arcadible/`:** add the icon, cover, banner, poster, and splash, and screenshots in `.arcadible/gallery/`. See [Your Store Page](https://developer.arcadible.com/publish/store-page).
- **`arcadible.toml`:** add a [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle), [`genres`](https://developer.arcadible.com/references/arcadible-manifest#game.genres), and the [`inputs`](https://developer.arcadible.com/references/arcadible-manifest#game.inputs) and [`orientation`](https://developer.arcadible.com/references/arcadible-manifest#game.orientation) your game supports.
Only `id` and `version` are required to deploy, so you can fill these in while you test.
## Deploy to staging
Deploy a prerelease version, such as `1.0.0-beta.1`, so the first build goes to [staging](https://developer.arcadible.com/concepts#channels) instead of to players. It's `version` in `package.json` when you have one, and in `arcadible.toml` otherwise. A `version` that [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) added is already one, `0.1.0-beta.1`. A release version goes straight to production:
```sh
arc deploy
arc play
```
> **`0.0.0` is a release version.** Tools such as create-vite write it, so deploying the project as it is sends its first build straight to players. Change `version` to a prerelease, such as `0.1.0-beta.1`, before you deploy.
## Fix what the first deploy finds
A game that runs on your machine can fail in the [Arcadible sandbox](https://developer.arcadible.com/build/runtime-limitations): case-sensitive file names, client-side routes, assets from other servers, precompressed builds, blocked dialogs, and saves kept in browser storage are the usual causes. Run [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) to find most of them on your machine, play the staging build, and work through [My game works locally, but not on Arcadible](https://developer.arcadible.com/troubleshooting#my-game-works-locally-but-not-on-arcadible), which has each one and its fix.
Fix them, bump the prerelease, and deploy again.
## Release it
Set a release version, such as `1.0.0`, and deploy. The game is unlisted until you set `visibility = "public"`; see [Going Public](https://developer.arcadible.com/publish/going-public).
## Next steps
- [Best Practices](https://developer.arcadible.com/build/best-practices): what makes a game feel at home on Arcadible.
- [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github): release from GitHub Actions instead of your machine.
- [How Arcadible Works](https://developer.arcadible.com/concepts): every term these docs use.
# How Arcadible Works
This page defines the parts of Arcadible and the terms the rest of the docs use. Each section is the one place a term is explained; other pages link here.
## The parts
- **The [Arcadible CLI](https://developer.arcadible.com/references/arcadible-cli)** (`arc`) creates projects, checks them, and deploys them, from your machine or from CI.
- **The store** is where players find games: [arcadible.com](https://arcadible.com) and the Arcadible desktop app show the same store pages.
- **The play page** at `play.arcadible.com` runs a game. The store's Play button opens it, and so does a play link.
- **[Arcadible Studio](https://developer.arcadible.com/studio)** is where you see your games, their builds and releases, and connect GitHub.
- **The [Arcadible SDK](https://developer.arcadible.com/references/arcadible-sdk)** is optional: it gives your game [saves](https://developer.arcadible.com/build/saves), which the play page keeps across versions.
## Your game
A game is a project with an [`arcadible.toml`](https://developer.arcadible.com/references/arcadible-manifest). Everything Arcadible knows about it comes from the project's files:
- **`README.md`** gives the title, tagline, bio, and description, in [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown).
- **`.arcadible/`** holds the store art: the icon, cover, banner, poster, splash, and screenshots.
- **`arcadible.toml`** holds the rest: the `id`, `version`, `handle`, genres, visibility, and how to build the game.
Together they resolve into the **manifest**, which [`arc inspect`](https://developer.arcadible.com/references/arcadible-cli/arc-inspect) prints and [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) checks. There are no forms to fill in on Arcadible: the store page is versioned with the game, reviewed in pull requests, and released with the build. See [Sources](https://developer.arcadible.com/references/arcadible-manifest#sources) for how the three files merge.
### `id` and handle
- **The [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id)** is a UUID that [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) writes, and it never changes. The account that first deploys an `id` owns the game.
- **The [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle)** is the game's name in links, such as `star-drift`. It's optional, unique among games, and can change. Without one, links use the `id`.
## Versions, releases, and builds
- **A version** is the game's [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version), in semver: `1.2.0`, or a prerelease such as `1.2.0-beta.1`.
- **A release** is one attempt to publish a version. [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) starts one, from your machine or through GitHub Actions. It ends published or failed. See [Releases](https://developer.arcadible.com/publish/releases).
- **A build** is a published version: your game's files, served exactly as they are from their own origin, `.arcadible.io`. Every build stays playable at its version link for as long as the game exists, and a build can't be deleted on its own.
**Each version publishes once.** To ship a change, bump the version and deploy again.
## Channels
The version decides where a build goes:

| Version | Channel | What it changes |
| --- | --- | --- |
| A release, such as `1.2.0` | `production` | What players get, and the store page, which is rebuilt from the release |
| A prerelease, such as `1.2.0-beta.1` | `staging` | Nothing players see. It's playable at its staging and version links |
Use staging to try a build on Arcadible itself before releasing it. A prerelease never changes the store page, so it can't make a game public. To see what it would publish, open the game's **Store** tab in [Arcadible Studio](https://developer.arcadible.com/studio#store) and choose **Staging**.
## Visibility
[`visibility`](https://developer.arcadible.com/references/arcadible-manifest#game.visibility) decides where a game is listed. Either way, anyone with a link can open the store page and play.
| Visibility | Listed in the store, search, the developer's page, and "More by" |
| --- | --- |
| `unlisted` (default) | No |
| `public` | Yes |
Like the rest of the store page, visibility changes with a production release.
## Links
Every link a game has, for Star Drift:
| Link | Opens |
| --- | --- |
| `https://arcadible.com/play/star-drift` | The store page |
| `https://play.arcadible.com/star-drift/` | The play page, with the latest `production` build |
| `https://play.arcadible.com/star-drift:staging/` | The latest `staging` build |
| `https://play.arcadible.com/star-drift:1.2.0/` | One version's build, in either channel |
| `https://arcadible.com/@` | The developer's page, with their public games |
- **The `id` works wherever the handle does,** such as `play.arcadible.com//`.
- **Play links are lowercase and end in a slash.** Other forms redirect to them.
- [`arc open`](https://developer.arcadible.com/references/arcadible-cli/arc-open) opens the store page, and [`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) the play page.
## The sandbox
On the play page, your game runs in the **Arcadible sandbox**: its build's own origin, embedded with restricted permissions and cross-origin isolation. A game that works on a local server can behave differently there; [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) runs it the same way on your machine. See [Runtime Limitations](https://developer.arcadible.com/build/runtime-limitations).
## Glossary
| Term | Meaning |
| --- | --- |
| Arcadible SDK | The optional library a game includes for saves. See [Saves](https://developer.arcadible.com/build/saves). |
| Arcadible sandbox | Where a game runs: its build's own origin, embedded in the play page. See [The sandbox](https://developer.arcadible.com/concepts#sandbox). |
| Arcadible Studio | Where developers see and manage their games. [studio.arcadible.com](https://studio.arcadible.com) |
| browser storage | `localStorage`, IndexedDB, and Cache Storage together. It's per build: see [Storage and saves](https://developer.arcadible.com/build/runtime-limitations#storage-and-saves). |
| build | A published version, served from its own origin. See [Releases](https://developer.arcadible.com/concepts#releases). |
| channel | `production` or `staging`, chosen by the version. See [Channels](https://developer.arcadible.com/concepts#channels). |
| deploy | What [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) does: check, build, pack, and start a release. |
| desktop app | The Arcadible desktop app, which shows the store and opens games in their own windows. |
| direct, `git` | The two deploy methods: an upload from your machine, or a release tag that GitHub Actions builds. See [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github). |
| handle | A game's name in links. See [`id` and handle](https://developer.arcadible.com/concepts#id-and-handle). |
| `id` | A game's permanent UUID. See [`id` and handle](https://developer.arcadible.com/concepts#id-and-handle). |
| manifest | The resolved description of a game, from the README, `.arcadible/`, and `arcadible.toml`. See [Your game](https://developer.arcadible.com/concepts#game). |
| play page | `play.arcadible.com//`, which runs a build. See [Links](https://developer.arcadible.com/concepts#links). |
| prerelease | A version with a suffix, such as `1.2.0-beta.1`, which goes to `staging`. |
| publish | What Arcadible does to a release that passes its checks: the build goes live in its channel. |
| release | One attempt to publish a version. See [Releases](https://developer.arcadible.com/publish/releases). |
| save | A named document the play page keeps for a game, across its versions. See [Saves](https://developer.arcadible.com/build/saves). |
| store page | A game's page in the store, built from its manifest. See [Your Store Page](https://developer.arcadible.com/publish/store-page). |
| visibility | Whether a game is listed. See [Visibility](https://developer.arcadible.com/concepts#visibility). |
# Best Practices for Games on Arcadible
Players judge games on Arcadible the way they judge games on any other platform: how fast they load, how they feel, and how polished they are. This guide covers what's specific to shipping a game on the web platform, in the order a player meets your game. It assumes you already know how to make a game.
Arcadible requires only what [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) checks. Everything here is advice: take what fits your game.
## Load fast
There's no install step: a new player downloads your game when they first open it. Set your targets for a phone on a mobile connection.

- **Paint within 1 second.** Put a small loading screen directly in `index.html`, with inline CSS, so it renders before any script loads. Use the same art as your [splash](https://developer.arcadible.com/references/arcadible-manifest#game.splash) so the transition from the store matches.
- **Be playable within about 5 seconds.** Load only the first screen, the first level, and their assets, and stream the rest during play, from your build or your own CDN.
- **Compress what you ship.** Choose compressed formats for textures, models, audio, and fonts, and minify your code. Most engines make these choices for you.
- **Show progress.** Base it on bytes loaded where you can, such as for a bundled file loaded with `fetch`. Browsers don't report progress for ES module imports, so for those, animate the loading screen so it doesn't look stuck.
- **Handle failure.** Time out and retry requests, and show an error with a retry option if loading fails.
Test with your browser's network panel throttled to a slow mobile connection and the cache disabled, which matches a new player's first load.
## Make the first touch part of the game
Browsers block audio, fullscreen, and pointer lock until the player interacts with the page. A common workaround is a "Tap to play" screen that exists only to collect that interaction. Instead, design the opening so the player's first natural input provides it:
- **Open on something interactive,** such as an attract-mode title, a controllable character, or a menu. The player's first action, whether a jump, a steer, or selecting "New Game", is also the interaction the browser requires.
- **Start audio on that input** by calling `audioContext.resume()` in your first `pointerdown` or `keydown` handler, and fade the music in.
- **Design the opening to work without sound.** Nothing plays before the first input, so use motion to prompt it.
## Get to play quickly
- **Give control early.** Make logos skippable, and keep story sequences and settings out of the way of the first moment of play.
- **Teach through play.** A new player should understand the goal within a few seconds without reading instructions. Introduce one mechanic at a time, and show gestures with animation.
- **Let players play before signing up.** Ask for an account once they have progress to keep.
## Fit every screen
Players use phones in both orientations, tablets, ultrawide monitors, and resizable desktop windows.

- **Handle resizing** with a [`ResizeObserver`](https://developer.mozilla.org/docs/Web/API/ResizeObserver) on the canvas, rather than reading the size once at startup.
- **Choose a scaling strategy,** whether that's letterboxing, extending the view, or reflowing the UI, and test the extremes: tall phones, square windows, 21:9 monitors.
- **Keep UI inside the safe area,** away from notches and home indicators, with [`env(safe-area-inset-*)`](https://developer.mozilla.org/docs/Web/CSS/env). Let the game world extend behind them.
- **Declare your [`orientation`](https://developer.arcadible.com/references/arcadible-manifest#game.orientation),** so the Arcadible desktop app can shape its window for you. In a browser, show a "rotate your device" screen if your game only works one way.
A canvas can't use `env()`. Copy the insets into custom properties, and read them in JavaScript when the canvas resizes:
```css
:root {
--safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
}
```
```js
const style = getComputedStyle(document.documentElement);
const safeTop = parseFloat(style.getPropertyValue("--safe-top"));
const safeBottom = parseFloat(style.getPropertyValue("--safe-bottom"));
```
## Support every input you declare
Declare your [`inputs`](https://developer.arcadible.com/references/arcadible-manifest#game.inputs) accurately, and support each one fully, menus included. Switch prompts to whichever device the player used last.
- **Keyboard:** use `event.code`, which names the physical key, so WASD stays in place on AZERTY and other layouts. Let players rebind keys.
- **Mouse:** for mouse-look, use [pointer lock](https://developer.mozilla.org/docs/Web/API/Pointer_Lock_API) with `requestPointerLock({ unadjustedMovement: true })` for raw input. Escape always releases the lock, so pause the game when `pointerlockchange` reports it's released.
- **Gamepad:** the [Gamepad API](https://developer.mozilla.org/docs/Web/API/Gamepad_API) has no button events, so poll `navigator.getGamepads()` every frame. With the `"standard"` mapping, button 0 is the bottom face button. Show Xbox, PlayStation, or Switch glyphs based on `gamepad.id`.
- **Touch:** use [Pointer Events](https://developer.mozilla.org/docs/Web/API/Pointer_events) for multi-touch. Make targets at least 44 CSS pixels, keep them clear of screen edges, which are reserved for system gestures, and hide touch controls on devices without touch.
## Turn off browser defaults
Browsers scroll the page, select text on a double tap, open a context menu on right-click, and zoom on a pinch, which a game rarely wants. The starters turn these off already, in their [`index.html`](https://github.com/arcadible/starters/blob/main/blank/.arcadible-public/index.html) and [`main.js`](https://github.com/arcadible/starters/blob/main/blank/.arcadible-public/main.js). If you started elsewhere, here's how.
In CSS, stop selection, scrolling, bouncing, and browser gestures:
```css
html,
body {
margin: 0;
height: 100%;
overflow: hidden;
overscroll-behavior: none;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
}
canvas {
touch-action: none;
}
```
In the viewport tag, turn off zoom, and extend the page behind the notch and other display cutouts:
```html
```
In JavaScript, suppress the context menu, and keep the keys your game uses from scrolling the page:
```js
const GAME_KEYS = new Set([
"Space",
"ArrowUp",
"ArrowDown",
"ArrowLeft",
"ArrowRight",
]);
addEventListener("contextmenu", (e) => e.preventDefault());
addEventListener("keydown", (e) => {
if (GAME_KEYS.has(e.code)) e.preventDefault();
});
```
- **Set a background color** that matches your loading screen, so players never see a white flash.
- **Style every element,** so no default browser buttons, links, or form controls show.
- **Hide the cursor** when it isn't in use, such as during gamepad play.
## Run smoothly
Aim for 60 frames per second, with a floor of 30 on the lowest-end device you support. Consistent frame times matter more than a high average.
- **Drive the loop with `requestAnimationFrame`,** and never assume 60 Hz. Displays run at 90, 120, 144 Hz and more, so step your simulation at a fixed rate and interpolate when drawing.
- **Avoid per-frame allocation.** Garbage collection pauses cause dropped frames. Pool objects, reuse arrays, and avoid creating closures in the loop.
- **Warm up during loading.** Compile shaders, upload textures, and decode audio before play, not the first time they're needed.
- **Move heavy work off the main thread** with [Web Workers](https://developer.mozilla.org/docs/Web/API/Web_Workers_API), and rendering too with [`OffscreenCanvas`](https://developer.mozilla.org/docs/Web/API/OffscreenCanvas). Arcadible games are [cross-origin isolated](https://developer.arcadible.com/build/runtime-limitations#cross-origin-isolation), so threads and `SharedArrayBuffer` are available.
- **Budget memory.** Browsers on low-end phones can terminate a page using a few hundred megabytes. A 2048 × 2048 RGBA texture uses 16 MB of GPU memory, regardless of its file size.
- **Mind the GPU.** Most engines pick WebGPU or WebGL for you. In WebGL, avoid calls that stall the GPU, such as `getError()` and synchronous `readPixels()`. See MDN's [WebGL best practices](https://developer.mozilla.org/docs/Web/API/WebGL_API/WebGL_best_practices).
### Scale resolution

Phones have a device pixel ratio of up to 3, which is nine times the pixels of a ratio of 1. Above 2, the visual gain rarely justifies the fill-rate cost.
- **Cap the pixel ratio** at 2, or lower for heavy 3D.
- **Scale dynamically.** Lower the render resolution when frame time rises, and raise it when there's headroom. Render the UI at full resolution.
- **For pixel art, scale by whole numbers instead.** Render to a small buffer, such as 320 × 180, and draw it at the largest whole-number multiple that fits the canvas at its full device resolution, with `imageSmoothingEnabled` off. Leave the pixel ratio uncapped: a small buffer costs little fill rate, and fractional scaling makes pixels uneven.
### Test on real hardware
Development machines are much faster than typical player devices. Test on the phones you have, including an older or cheaper one if you can.
## Pause, save, and resume
A page can be hidden or closed at any time, without warning.
- **Pause on `visibilitychange` and `blur`,** audio included, and resume to a pause menu. In the desktop app, your game [keeps running](https://developer.arcadible.com/build/runtime-limitations#browsers-and-the-desktop-app) in the background unless you pause it.
- **Save continuously,** at checkpoints and when the page is hidden. `visibilitychange` and `pagehide` are the last events you can count on.
- **Keep progress in [saves](https://developer.arcadible.com/build/saves),** which carry over to your next version, where browser storage doesn't.
## Other recommendations
- **Accessibility:** reduce screen shake and flashing when [`prefers-reduced-motion`](https://developer.mozilla.org/docs/Web/CSS/@media/prefers-reduced-motion) is set, don't convey information by color alone, and offer subtitles and control remapping.
- **Language:** pick a default from `navigator.languages`, let players change it, and format numbers and dates with [`Intl`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl).
- **Network:** let the game start without a server, retry with backoff, and show players a message they can act on rather than a raw error ([Networking](https://developer.arcadible.com/build/runtime-limitations#networking)).
## Next steps
- [Runtime Limitations](https://developer.arcadible.com/build/runtime-limitations): what the platform allows, and its checklist.
- [Testing Your Game](https://developer.arcadible.com/build/testing): staging, the desktop app, and real devices.
- [Your Store Page](https://developer.arcadible.com/publish/store-page): the art and text players judge before they play.
- [Going Public](https://developer.arcadible.com/publish/going-public): the checklist before players can find your game.
# Runtime Limitations for Games on Arcadible
Games run in the Arcadible sandbox. WebGL, WebGPU, WebAssembly, threads, audio, gamepads, fullscreen, and pointer lock are available. Popups, native dialogs, downloads, top-level navigation, the camera, the microphone, and location are not.
The restrictions isolate games from each other and from Arcadible, and keep behavior consistent wherever Arcadible runs games.
## How your game is loaded
Every play link, such as `https://play.arcadible.com/star-drift/`, is a page that embeds your build in a full-size, sandboxed frame.

- **Each build has its own origin.** Every deploy is published to a new `https://.arcadible.io/`, where `` is unique to that build. Two versions of your game are two different origins, which matters for [saves](https://developer.arcadible.com/build/runtime-limitations#storage-and-saves) and for [your servers](https://developer.arcadible.com/build/runtime-limitations#networking).
- **Your game is at the root.** `deploy_dir` is served at `/`, so relative and root-relative paths both work. Prefer relative paths, which also work when the build is served from a subdirectory during local testing.
- **The play link's query string and hash aren't passed on.** The frame always loads `https://.arcadible.io/`, whatever follows the play link.
- **The desktop app uses the same play page,** so everything here applies there too, with a few [differences](https://developer.arcadible.com/build/runtime-limitations#browsers-and-the-desktop-app).
- **Play pages can't be embedded** in other sites.
- **`/arcadible/` is Arcadible's.** Every build's origin serves the [Arcadible SDK](https://developer.arcadible.com/references/arcadible-sdk#install) at `/arcadible/sdk/v1.js`, with its source map, and nothing else in `/arcadible/`, so your build can't have an `arcadible` folder at its root. See [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir).
## Static files
Your build is served as static files, with no server-side code and no configuration.
| Limit | Detail |
| --- | --- |
| Size | See [Limits](https://developer.arcadible.com/references/limits#package): 64 MB compressed, 256 MB uncompressed, and 10,000 files. |
| Entry point | `index.html` at the root of `deploy_dir`. |
| Directory URLs | A path ending in `/` serves that directory's `index.html`. The same path without the trailing slash isn't redirected, and fails. |
| No rewrites | Every URL maps to exactly one file. Client-side routes like `/level/3` return an error, so use the hash (`#/level/3`) or in-memory state instead. |
| Case-sensitive paths | `Ship.png` and `ship.png` are different files. A build that works on macOS or Windows can fail on Arcadible because of a case mismatch. |
| No custom headers | You can't set headers per file. Builds that rely on the server adding `Content-Encoding` to precompressed `.br` or `.gz` files won't load; see below. |
| No server-side code | PHP, API routes, and server functions don't run. Host your backend separately; see [Networking](https://developer.arcadible.com/build/runtime-limitations#networking). |
### Compressed engine builds
Some engines, including Unity and some Godot templates, can emit files that are already Brotli- or gzip-compressed and expect the server to label them with `Content-Encoding`. Arcadible serves each file as it is, so either:
- **disable compression** in the engine's build settings, or
- **enable a decompression fallback,** such as Unity's _Decompression Fallback_ option, which decompresses the files in JavaScript.
## The sandbox
The frame's `sandbox` allows scripts, same-origin storage, pointer lock, and orientation lock. No other sandbox flags are set.
| Feature | Status | What to do instead |
| --- | --- | --- |
| JavaScript, WebAssembly, workers | Allowed | |
| `localStorage`, IndexedDB, Cache Storage | Allowed | Read [Storage and saves](https://developer.arcadible.com/build/runtime-limitations#storage-and-saves) first. |
| Pointer lock | Allowed | Request it from a click or key press. |
| Screen orientation lock | Allowed | Prefer the [`orientation`](https://developer.arcadible.com/references/arcadible-manifest#game.orientation) field. |
| `window.open()`, `target="_blank"` links | Blocked | Show the URL, or a QR code, in the game. |
| Navigating the top window | Blocked | Keep the game on a single page. |
| `alert()`, `confirm()`, `prompt()`, `print()` | Blocked | Draw your own dialogs. Blocked calls return immediately without showing anything. |
| `beforeunload` prompts | Blocked | Save continuously instead of asking players to stay. |
| `
# Testing Your Game on Arcadible
Test in two places: locally while you build, with [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve), and on Arcadible's staging channel before you release, the only way to run it on Arcadible before players get it.
## Locally
[`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) runs your game on your machine the way Arcadible runs it. It builds, then serves `deploy_dir` framed in a local play page, with the sandbox and the [Arcadible SDK](https://developer.arcadible.com/references/arcadible-sdk), and reloads the game when the build changes. For the starters:
| Starter | While you edit | To check the build players get |
| --- | --- | --- |
| Blank | `arc serve` | The same: there's no build |
| Three.js, Phaser, PixiJS, Babylon.js | `npm run dev`, with `arc serve --url http://localhost:5173` beside it | `arc serve` |
Run `npm install` first in the engine starters. `npm run dev` is Vite's dev server, which reloads as you edit, and `arc serve --url` frames it in the local play page, with the sandbox and the SDK's host ([Framing a dev server](https://developer.arcadible.com/references/arcadible-cli/arc-serve#url)). `arc serve` on its own builds once, and serves what [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) would publish.
Any other local server, such as a dev server opened on its own, isn't the [Arcadible sandbox](https://developer.arcadible.com/build/runtime-limitations):
- **Paths aren't case-sensitive** on most machines: your machine may find `Ship.png` when the game asks for `ship.png`. Arcadible won't.
- **Cross-origin isolation is off,** unless the server sends Arcadible's headers, as the engine starters' `vite.config.js` does: assets from other servers can load, and be blocked on Arcadible.
- **The sandbox's limits are off:** dialogs, popups, and form submission work, and are blocked on Arcadible.
What no local server reproduces is a fresh origin per version: browser storage persists locally across builds, but each version on Arcadible starts with it empty. [Saves](https://developer.arcadible.com/build/saves) carry over on Arcadible and under `arc serve`; see [Locally](https://developer.arcadible.com/build/saves#locally).
## On staging
Deploy a prerelease to try a build on Arcadible itself, without changing what players get:
```sh
arc deploy # with a version such as 1.2.0-beta.1
arc play -r staging # the latest prerelease
```
A prerelease goes to the `staging` channel. It's playable at its link, isn't shown in the store, and doesn't change the store page. To see the store page it would publish, open the game's **Store** tab in [Arcadible Studio](https://developer.arcadible.com/studio#store) and choose **Staging**. Its play page shows that version's own title, icon, and orientation, as the installed game does. Share the staging link with testers:
```text
https://play.arcadible.com/star-drift:staging/
```
Deploy to staging as you develop, not only before a release, so each problem shows up soon after the change that caused it. See [Channels](https://developer.arcadible.com/concepts#channels).
## A specific version
Every build stays playable at its version link, in either channel. Use it to compare two versions, or to find the release that broke something:
```sh
arc play -r 1.1.0
```
```text
https://play.arcadible.com/star-drift:1.1.0/
```
## On devices
- **Phones and tablets:** open the staging link on the device. Test touch, rotation, and the safe area on real hardware; see [Best Practices](https://developer.arcadible.com/build/best-practices#test-on-real-hardware).
- **The desktop app:** [`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) opens the game in the Arcadible desktop app when it's installed. Games there run in their own window, with a few [differences](https://developer.arcadible.com/build/runtime-limitations#browsers-and-the-desktop-app) from a browser.
- **Browsers:** `arc play --browser` opens the play page in your default browser. Test the browsers your players use, including Safari.
## In CI
[`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) exits with `1` when the project has issues, so it works as a check on every pull request:
```yaml title=".github/workflows/validate.yml"
on: pull_request
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm install -g arcadible
- run: arc validate
```
`arc validate` needs no login. To release from CI, see [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github).
## Checklist
- [ ] The game runs under [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve), not only under a dev server
- [ ] A prerelease plays on staging in a browser and in the desktop app
- [ ] Tested on a phone, with touch and both orientations your game supports
- [ ] No errors in the browser's console on the play page
- [ ] Saves carry over from one prerelease to the next, if the game uses them
# Game Engines on Arcadible
Arcadible runs any game that builds to a folder of static files with an `index.html`. Whatever you build with, the setup is in `arcadible.toml`: the command that builds the game, if any, and the folder it builds into.
```toml title="arcadible.toml"
[workflow]
build = "npm run build"
deploy_dir = "dist"
```
See [`workflow`](https://developer.arcadible.com/references/arcadible-manifest#workflow) for details, and [Runtime Limitations](https://developer.arcadible.com/build/runtime-limitations) for the environment your game runs in.
## Starter templates
Each starter is a complete project that deploys as it is:
| Template | Stack | Create it |
| --- | --- | --- |
| `arcadible/starters/blank` | Blank canvas | `arc init star-drift -y` |
| `arcadible/starters/threejs` | Three.js | `arc init star-drift -t arcadible/starters/threejs` |
| `arcadible/starters/phaser` | Phaser | `arc init star-drift -t arcadible/starters/phaser` |
| `arcadible/starters/pixijs` | PixiJS | `arc init star-drift -t arcadible/starters/pixijs` |
| `arcadible/starters/babylonjs` | Babylon.js | `arc init star-drift -t arcadible/starters/babylonjs` |
Each keeps the direction its mark spins in as a [save](https://developer.arcadible.com/build/saves), so it carries over to the next version: the engine starters through the package, `@arcadible/sdk`, and the blank starter through the hosted script. Remove it if your game has nothing to save.
For anything else, create the project with its own tools, then run [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) in it. See [Bring an Existing Game](https://developer.arcadible.com/existing-game).
## JavaScript and TypeScript
The engine starters build with [Vite](https://vite.dev). Set its `base` to `"./"` so the build uses relative paths, and point `deploy_dir` at the folder it writes, `dist` by default. `package.json`'s `build` script runs as `npm run build`:
```js title="vite.config.js"
export default { base: "./" };
```
```toml title="arcadible.toml"
[workflow]
deploy_dir = "dist"
```
The engine starters set Vite's `outDir` to `.arcadible-public` instead, the default `deploy_dir`, so a new project keeps the same layout as the blank starter. Their Vite servers also send Arcadible's isolation headers, so `npm run dev` is cross-origin isolated, as on Arcadible, and [`arc serve --url`](https://developer.arcadible.com/references/arcadible-cli/arc-serve#url) can frame it.
| Library | What it is | Docs |
| --- | --- | --- |
| [Three.js](https://threejs.org) | A 3D renderer, with WebGL 2 or WebGPU. Physics, audio, and game structure are up to you. MIT. | [Manual](https://threejs.org/manual/) |
| [Phaser](https://phaser.io) | A 2D framework, with scenes, physics, tilemaps, input, and audio. MIT. | [Docs](https://docs.phaser.io) |
| [PixiJS](https://pixijs.com) | A 2D renderer, with WebGL and WebGPU. Game structure, physics, and audio are up to you. MIT. | [Guides](https://pixijs.com/8.x/guides) |
| [Babylon.js](https://www.babylonjs.com) | A 3D engine, with physics, a GUI, animation, and audio, on WebGL 2 or WebGPU. Apache 2.0. | [Docs](https://doc.babylonjs.com) |
With Babylon.js, import each module from its own path, such as `@babylonjs/core/Engines/engine`, as the starter does. Importing from `@babylonjs/core` itself pulls in the whole engine, several megabytes, however little the game uses.
## Engines with web exports
Export a web build into a folder in your project, and point `deploy_dir` at it. An engine that compresses its export and expects the server to label the files, as Unity does by default, needs that turned off; see [Compressed engine builds](https://developer.arcadible.com/build/runtime-limitations#compressed-engine-builds).
> **These engines' saves don't carry over to your next version.** The Arcadible SDK doesn't currently have a plugin for them, so their saves stay in browser storage, which [starts empty in each version](https://developer.arcadible.com/build/runtime-limitations#storage-and-saves).
For example, [Godot](https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_web.html), with its export templates installed and a **Web** export preset:
```toml title="arcadible.toml"
[workflow]
build = "godot --headless --export-release Web build/web/index.html"
deploy_dir = "build/web"
```
Its exports with threads work too, since Arcadible is cross-origin isolated. Godot 4 doesn't currently export C# projects to the web ([Godot](https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_web.html)).
## Native code and WebAssembly
Code in C, C++, or Rust compiles to WebAssembly, with an HTML page and JavaScript glue code. Name the page `index.html`, and point `deploy_dir` at its folder.
For example, a [Bevy](https://bevy.org) game in Rust, built with [Trunk](https://trunk-rs.github.io/trunk/):
```toml title="arcadible.toml"
[workflow]
build = "trunk build --release --public-url ./"
deploy_dir = "dist"
```
WebAssembly from Rust is large before optimization: build with `opt-level = "z"` or `"s"`, enable LTO, and run `wasm-opt`.
## Your own engine
An engine is optional. The [blank starter](https://github.com/arcadible/starters/tree/main/blank) is a canvas and a script, with Canvas 2D, WebGL 2, and WebGPU for graphics, Web Audio for sound, and the Gamepad and Pointer Events APIs for input. It's the smallest download.
## Next steps
- [Runtime Limitations](https://developer.arcadible.com/build/runtime-limitations): the environment your game runs in.
- [Saves](https://developer.arcadible.com/build/saves): keep players' progress across versions.
- [Testing](https://developer.arcadible.com/build/testing): try your game locally and on staging.
# Working with AI Agents on Arcadible
Coding agents can build, check, and publish an Arcadible game with the same `arc` commands you use. This page covers how an agent reads these docs, an `AGENTS.md` to add to your project, and how `arc` behaves when an agent runs it.
## Give the agent the docs
These docs are published as text:
- **[llms.txt](https://developer.arcadible.com/llms.txt)** lists every page as Markdown, with a one-line description.
- **[llms-full.txt](https://developer.arcadible.com/llms-full.txt)** has every page in one file.
- **Add `.md` to any page's URL** for its Markdown, such as [/references/arcadible-manifest.md](https://developer.arcadible.com/references/arcadible-manifest.md).
Point the agent at the page for the file it's changing, rather than all of them. The table in the `AGENTS.md` below does that.
## Add an `AGENTS.md`
Agents that support [`AGENTS.md`](https://agents.md) read it before working in a project. The [starters](https://developer.arcadible.com/build/game-engines) include one made for them. For a project of your own, save this at its root, and adjust it to your game, such as its commands and `deploy_dir`.
**It holds only what doesn't change, and links to these docs for everything else.** An `AGENTS.md` is copied into each project and isn't updated after, so an agent reading an old one still follows the current docs, which it reads as it works. Keep yours the same way: add what's true of your game, and link to these docs rather than copying from them.
````md title="AGENTS.md"
# AGENTS.md
This project is a game for Arcadible, a platform for publishing games. Build it with anything that runs in a browser: if it builds to a folder with an `index.html`, the `arc` CLI checks and deploys it, whatever the engine. Players play it wherever Arcadible is installed, and in any browser.
**This file was written when the project was created, and Arcadible's docs are kept current.** Where they differ, follow the docs. Every page is listed in https://developer.arcadible.com/llms.txt, and is all in https://developer.arcadible.com/llms-full.txt.
## Read the docs first
Read the page that covers what you're changing, every time, rather than relying on what you remember of it.
| Before you change | Read |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The game | https://developer.arcadible.com/build/best-practices.md and https://developer.arcadible.com/build/runtime-limitations.md |
| How it saves progress | https://developer.arcadible.com/build/saves.md, then https://developer.arcadible.com/references/arcadible-sdk/saves.md and its codes in https://developer.arcadible.com/references/arcadible-sdk.md |
| `arcadible.toml` | https://developer.arcadible.com/references/arcadible-manifest.md |
| `.arcadible/` | https://developer.arcadible.com/publish/store-page.md and https://developer.arcadible.com/references/arcadible-manifest.md |
| `README.md` | https://developer.arcadible.com/references/arcadible-markdown.md |
| How you deploy, play, or release | https://developer.arcadible.com/references/arcadible-cli.md and https://developer.arcadible.com/publish/releases.md |
| How it releases from GitHub | https://developer.arcadible.com/publish/deploying-from-github.md |
## Project structure
| Path | Contains |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `arcadible.toml` | The game's id, version, and settings. Never change `id`. If it's missing, `arc init` adds one without scaffolding. |
| `README.md` | The store page: the title, tagline, bio, and the description between the first and last `---`. |
| `.arcadible/` | The store art, with fixed file names. |
| `deploy_dir` | The game players load (`workflow.deploy_dir`, `.arcadible-public` by default), with `index.html` at its root. When the project has a build, the build writes it: don't edit it by hand. |
## Commands
```sh
npm run dev # the project's dev server, when it has one
arc serve # the game as Arcadible serves it, framed in a play page
arc validate # the checks arc deploy runs, with a link to each broken rule
arc deploy # validate, build, publish, and wait until it's published
```
Every `arc` command is in https://developer.arcadible.com/references/arcadible-cli.md. Add `--json` to read a command's result as one JSON document on stdout. A command that fails exits with 1, and its JSON has `error.code`: act on the code, and look it up in https://developer.arcadible.com/references/errors.md, which says what to do.
## What doesn't change
- **Each version starts with empty browser storage,** since each build has its own origin. Keep progress in saves, through the Arcadible SDK: the `@arcadible/sdk` package for a game with a build step; or, for a game with none, the hosted script, ``, which Arcadible and `arc serve` serve.
- **The game runs in the Arcadible sandbox,** which `arc serve` reproduces and other local servers don't. Read the runtime limitations before changing how it loads files, stores data, or talks to servers.
- **`/arcadible/` is Arcadible's.** Don't put an `arcadible` folder at the root of `deploy_dir`.
## Rules
- Run `arc validate` after every change to `arcadible.toml`, `README.md`, or `.arcadible/`. It exits with 1 when there are issues.
- Bump `version` before every deploy: in `package.json` when there is one, otherwise in `arcadible.toml`. Each version publishes once. A prerelease, such as `1.2.0-beta.1`, goes to `staging`, and any other version to `production`.
- Deploy prereleases to test. Deploy a release version only when the user asks to release: it's what players get, and it publishes the store page.
- Change `visibility` to `public` only when the user asks. It lists the game in the store and in search.
- When a code's entry says the user must act, such as logging in or connecting GitHub in Arcadible Studio, ask them, wait until they say it's done, then run the command again.
````
## Running `arc` from an agent
The CLI is built to be run by agents as well as people. See [Output](https://developer.arcadible.com/references/arcadible-cli#output) for the full rules.
- **Pass `--json`** to read a command's result (every command but `login`, `logout`, `open`, `play`, and `serve` takes it). stdout holds exactly one JSON document, and stderr stays empty.
- **Check the exit code.** Every command exits with `0` when it succeeds and `1` when it fails, including a deploy whose release fails.
- **Act on `error.code`,** not the message. Each [code](https://developer.arcadible.com/references/errors#codes) has an entry in Errors, with what to do, and codes don't change between versions.
- **Create projects without prompts.** [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) asks questions in a terminal. Pass `-y` to accept the defaults, or `-t ` to choose a template.
- **Leave logging in to the user.** [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) needs a browser. Once the user has logged in, [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) uses the saved session.
- **Ask the user for what only they can do:** log in, connect the GitHub App and the repository in Arcadible Studio, and choose a new `handle` or game `id`. Each code's entry in [Errors](https://developer.arcadible.com/references/errors) says when that's what it needs.
- **Let `arc deploy` wait.** It returns when the release is published or has failed, so the agent knows the result without polling. See [Releases](https://developer.arcadible.com/publish/releases).
## Next steps
- [Getting Started](https://developer.arcadible.com/getting-started): the walkthrough the `AGENTS.md` summarizes.
- [Arcadible CLI](https://developer.arcadible.com/references/arcadible-cli): every command and option.
- [Releases](https://developer.arcadible.com/publish/releases): how to fix a failed release.
# Releases on Arcadible
A [release](https://developer.arcadible.com/concepts#releases) is one attempt to publish a version of your game. This page covers what happens between [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) and a published build, where you hear the result, and what to do when a release fails.
## From deploy to published
Every release ends published or failed:
1. **Deployed.** [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) uploads a zip of your game, or `arc deploy git` pushes a release tag that GitHub Actions builds (see [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github)).
2. **Processed.** Arcadible reads the zip, checks that its manifest matches the game and version being released, and stores the build.
3. **Published.** The build is playable at its version link, such as `play.arcadible.com/star-drift:1.2.0/`. A release version also updates your store page and becomes what players get. Changes can take up to 10 minutes to reach all of Arcadible.
A direct release is published within a few minutes of its upload. A `git` release is published after its GitHub Actions run, which can take several minutes, depending on the build. A release that hasn't finished within an hour fails as `release_expired`.
## Where you hear about it
| Where | Published | Failed |
| --- | --- | --- |
| [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) | `Published v1.2.0 to production.` (or `Built and published …`), with its links | `Release failed.`, with what happened and the reason, and exits with `1` |
| Email | "Release Published", with the version, the channel, and that version's title and icon | "Release Failed", with what happened, the reason, how to fix it, and Studio |
| GitHub check (`git` only) | The Arcadible check on the tagged commit passes | The check fails, with what happened, the reason, and how to fix it |
| Arcadible Studio | The build, on the game's **Builds** tab | Above the game's builds, and on the **Builds** page, until a later release publishes |
- **`arc deploy` waits by default.** It exits with `1` when a release fails, so a script or CI job fails with it. See [Waiting for the release](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release).
- **Emails go to your account's address,** for every release, whichever way you deployed.
- **Studio shows where releases stand above the builds:** a line for each release on its way, and **Releases to fix: N** for the failures since the game's last published release. **Show why** opens each with what happened, its reason, and a link to its fix. **Builds ▸ Failed** keeps every failure. A first release that fails before the game exists isn't in Studio's game pages; open it from the email's link.
- **The GitHub check passes once Arcadible accepts the build,** before the store page is written. If a release fails after that, as `internal`, `game_unavailable`, or `release_expired`, the email says so and the check doesn't change.
## When a release fails
A version is taken only when its build is published. **Fix the cause and deploy the same version again,** unless the reason is `version_exists`. Each reason, and its fix, is in [Release failure reasons](https://developer.arcadible.com/references/errors#release-reasons).
## Rolling back
To roll back, deploy the earlier code with a higher version. Arcadible doesn't currently roll back to an earlier build itself: builds can't be deleted one by one, and each stays playable at its version link until its game is deleted. Release limits, such as 30 builds an hour and 100 games an account, are in [Limits](https://developer.arcadible.com/references/limits#releases).
## Next steps
- [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github): release from GitHub Actions, with a check on every release commit.
- [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy): both deploy methods, waiting, and `--json`.
- [Errors](https://developer.arcadible.com/references/errors): every message, and every reason a release fails.
# Your Store Page on Arcadible
Arcadible builds your store page from your project: the text from your README, the art from `.arcadible/`, and the details from `arcadible.toml`. This page shows where players see each part, in the Arcadible desktop app and on the web, so you can write and draw for each place. Where art appears can change as the store does; provide every piece, and the store shows what fits. The rules for every field are in the [manifest reference](https://developer.arcadible.com/references/arcadible-manifest).
## Where players see your game
| Place | What it shows |
| --- | --- |
| Lists: Explore's Featured and Popular (Popular numbered), the library, a player's games, "More by" you | The poster, with the icon, the title, and the tagline (your genres without one) under it |
| Search results | The icon, the title, the tagline, and Play, with your first three screenshots under it, or the cover without screenshots |
| Your store page | Everything below, from the header down |
| Link previews | The cover |

Arcadible chooses the games in Featured and Popular. Only public games appear in the store and in search; unlisted games are playable by link only. See [`visibility`](https://developer.arcadible.com/references/arcadible-manifest#game.visibility).
Search matches your title first, then your short title, handle, genres, and tagline, then your bio and description. A word's start is enough, so `dri` finds Star Drift, and a small typo in the title still matches.
Your store page has, in order:
1. **The header:** the icon, the title, the tagline, your genres, and your name as the developer, then Play, Like, and Share, and Manage in Studio for you, or **•••** ▸ **Report** for anyone else. Your splash may fill the header behind the text, and its video may play there once.
2. **The facts:** plays, likes, the audience, the developer, your first genre, and your controls.
3. **The screenshots,** scrolling sideways. Each opens full screen. A screenshot can be an MP4, shown by its first frame with a play mark, and played full screen.
4. **About:** the description, and the audience and AI notices beside it. A long description shows its start, with More to read the rest.
5. **Information:** the developer, genres, controls, orientation, when the game was released and last updated, and the version.
6. **More by you:** your other public games.

## Text
- **Write a tagline.** It's the line under your title everywhere the game is listed, and may be the headline where it's featured. Without one, lists show your genres instead. See [`tagline`](https://developer.arcadible.com/references/arcadible-manifest#game.tagline).
- **Make the bio the pitch.** It's shown in About when there's no description, and may appear, cut to two lines, where the game is featured. See [`bio`](https://developer.arcadible.com/references/arcadible-manifest#game.bio).
- **Lead the description with what the game is.** About shows its first lines until a player presses More, so put the premise and controls before credits and patch notes. See [`description`](https://developer.arcadible.com/references/arcadible-manifest#game.description).
## Art
- **Keep the icon's subject in the center,** inside the [edge margin](https://developer.arcadible.com/publish/store-page#edge-margin). The store rounds the icon's corners, and shows it as small as 36 CSS pixels in lists. See [`icon`](https://developer.arcadible.com/references/arcadible-manifest#game.icon).
- **Provide a poster.** Lists show the poster. Without one, they show your icon on a tinted background. See [`poster`](https://developer.arcadible.com/references/arcadible-manifest#game.poster).
- **Provide a cover.** Search results without screenshots and link previews show it. See [`cover`](https://developer.arcadible.com/references/arcadible-manifest#game.cover).
- **Provide a banner.** The store may use it wherever wide art fits, such as a featured spot. See [`banner`](https://developer.arcadible.com/references/arcadible-manifest#game.banner).
- **Keep the splash free of detail.** It may appear behind text, as may its video. See [`splash`](https://developer.arcadible.com/references/arcadible-manifest#game.splash).
- **Keep text out of the art,** beyond the title on the cover, banner, and poster. The store shows your title and tagline as text beside it. See [Text in art](https://developer.arcadible.com/publish/store-page#text-in-art).
- **Make an art video end on its PNG.** Each piece of art can have a [video](https://developer.arcadible.com/references/arcadible-manifest#art-videos), which may play once without sound and then fade back to the PNG. End it on a frame close to the PNG so the change doesn't jump, and keep the PNG, which shows wherever video doesn't.
- **Show the banner on GitHub too,** by referencing it at the top of your README: ``.
### Text in art
Art is shown at many sizes, down to a thumbnail, and the store shows your title and tagline as text beside it.
- **Icon and splash: no text.** The icon may be shown as small as a list row, and the splash may sit behind text.
- **Cover, banner, and poster: the title at most.** A stylized title or logo is fine. Leave out taglines, slogans, credits, and other small text.
### Edge margin
The store may crop art to fit a place, or draw over its edges behind a fade. The edge margin keeps what matters in view wherever the art appears:

- **The edge margin:** keep the subject, the title, and anything else that matters at least 10% of the art's shorter side in from every edge, the same distance on all four sides. Let the art itself run to the edges.
- **Inside it, compose freely.** Place the title and subject where they suit your art, not in a fixed spot.
The recommended sizes are about twice what the store draws, so art stays sharp on high-density screens. At those sizes:
| Art | Recommended size | Edge margin |
| --- | --- | --- |
| Icon | 1024 × 1024 | 102 px |
| Cover | 1536 × 1024 | 102 px |
| Banner | 3360 × 1680 | 168 px |
| Poster | 1024 × 1536 | 102 px |
| Splash | 1920 × 1080 | 108 px |
| Screenshots | 1920 × 1080, or 1080 × 1920 for `_phone` | — |
Screenshots have no edge margin: they're gameplay, framed as you capture it. The [starters](https://developer.arcadible.com/getting-started)' placeholder art is drawn at these sizes, with the edge margin marked, so you can paint over it.
## Screenshots
- **Show real gameplay** in screenshots and the trailer, and start the trailer with play rather than logos.
- **Name screenshots `screenshot_`.** Only those files appear on the store page, ordered by number. Other gallery files are for the description. See [Screenshots](https://developer.arcadible.com/references/arcadible-manifest#screenshots).
- **Put your best three first.** Search results show the first three under your game.
- **Add phone screenshots** named `screenshot__phone`. Phones and tablets show those instead of the others when there are any.
## Details
- **List your main genre first.** The facts show the first genre, and the game may appear on the page of every genre you list. See [`genres`](https://developer.arcadible.com/references/arcadible-manifest#game.genres).
- **Set `audience` and `ai` to match the game.** The store shows the audience as a notice on your store page, and labels a `generative` game. See [`audience`](https://developer.arcadible.com/references/arcadible-manifest#game.audience) and [`ai`](https://developer.arcadible.com/references/arcadible-manifest#game.ai).
- **Declare every input the game supports.** The facts and Information list them as the game's controls. See [`inputs`](https://developer.arcadible.com/references/arcadible-manifest#game.inputs).
### Choosing an audience
[`audience`](https://developer.arcadible.com/references/arcadible-manifest#game.audience) is `everyone` or `mature`, and choosing is a judgment call:
- **Rate the most intense content a player can reach**, not a typical session. A graphic scene late in the game counts as much as the opening minutes.
- **Consider how content is presented.** Stylized, comic, or fantasy conflict usually fits `everyone`. Realistic or lingering violence, gore, sexual content, and frequent strong language usually point to `mature`.
- **Include everything the game can show:** cutscenes, dialogue, audio, and any text or images that players share with each other.
- **When you're unsure, choose `mature`.** Players who see `everyone` expect that label to hold.
- **Update it with the game.** If an update adds more intense content, change the audience in the same release.
### Disclosing AI
[`ai`](https://developer.arcadible.com/references/arcadible-manifest#game.ai) is `generative` when the game's content was made with generative AI, which the store page labels, or `unused`:
- **Think about what players see and hear:** art, animation, music, sound effects, voices, dialogue, text, and levels.
- **Code doesn't count.** Code written with an AI assistant isn't disclosed. Only the media and content it ships or generates are.
- **Partly generated still counts.** Content that started as AI output and was edited or painted over by hand is `generative`.
- **Content generated during play counts too**, such as dialogue or levels a model creates while the game runs.
- **When you're unsure, choose `generative`.** Players who don't see the label expect the game to be free of generated content.
- **Set it explicitly.** Left out, no label is shown, but the game's AI use is recorded as `unknown`.
### Declaring inputs
[`inputs`](https://developer.arcadible.com/references/arcadible-manifest#game.inputs) lists how the game can be played, and the store page shows them as its controls:
- **List each input that can play the whole game by itself.** A game that plays with the keyboard alone and with the mouse alone lists `keyboard` and `mouse`.
- **Use `keyboard-mouse` only when the game needs both at once,** such as moving with keys while aiming with the mouse.
- **Support every input you list.** See [Best Practices](https://developer.arcadible.com/build/best-practices#support-every-input-you-declare).
## Before the first release
Only release versions change the store page. A `staging` build doesn't, so a prerelease shows nothing early: until a game's first production release, its store page shows only its name and "Not released yet". Logged in as its developer, you also see that the first production release publishes the page, with Manage in Studio.
To see what a prerelease would publish, open the game's **Store** tab in [Arcadible Studio](https://developer.arcadible.com/studio#store) and choose **Staging**. See [Releases](https://developer.arcadible.com/publish/releases).
## Checklist
- [ ] A tagline, and a bio that reads as the pitch in two lines
- [ ] A description that starts with the premise and controls
- [ ] An icon with its subject centered, and no text
- [ ] A cover, a banner, a poster, and a splash, at the recommended sizes, with what matters inside the edge margin
- [ ] Screenshots named `screenshot_`, the best three first, and `_phone` ones for a portrait game
- [ ] The main genre first, and `audience`, `ai`, and `inputs` set to match the game
# Deploying Games from GitHub
[`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) releases your game from GitHub instead of from your machine. It pushes a release tag, GitHub Actions builds the game from that commit, and Arcadible publishes it. Every release maps to a commit, and anyone who can push to the repository can release the game without an Arcadible login, which is how a team shares deploys.

## Set it up
1. **Deploy the game once with [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy).** A release from GitHub publishes builds of a game that's already on Arcadible. It can't create one.
2. **Push the project with the release workflow.** The starters include `.github/workflows/arcadible-release.yml`. In a project of your own, add it from [What the workflow does](https://developer.arcadible.com/publish/deploying-from-github#what-the-workflow-does), and also:
- commit a lockfile, if there's a `package.json`;
- add `.arc/` to `.gitignore`, since `arc deploy` writes its zip there, and `arc deploy git` refuses uncommitted files.
3. **Install the Arcadible GitHub App.** In [Arcadible Studio](https://studio.arcadible.com), go to **Settings ▸ Integrations**. If it's already installed on your account or organization, choose **Connect an existing installation**.
4. **Connect the repository** on the game's **Repository** tab, and choose **Connect**. A repository deploys one game, whose `arcadible.toml` is at the repository's root: the workflow builds from there.
To make `git` the default for `arc deploy`, add a `.arcrc`:
```ini title=".arcrc"
[deploy]
method = git
```
## Release a version
Bump [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version), commit, and run:
```sh
arc deploy git
```
The CLI checks that everything is committed, that the release workflow is there, that `origin` is reachable, that the version isn't published, and that `origin` is the game's connected repository, then pushes the tag `arcadible/release/v` on `HEAD`. It waits while GitHub Actions builds the release and Arcadible publishes it, then prints `Published v1.2.0 to production.` See [`git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) for every check and message.
## What the workflow does
The starter's workflow runs on any tag matching `arcadible/release/v*` and calls Arcadible's reusable workflow, `arcadible/workflows/.github/workflows/release.yml@v1`:
```yaml title=".github/workflows/arcadible-release.yml"
name: Arcadible Release
on:
push:
tags:
- "arcadible/release/v*"
permissions:
contents: write
jobs:
release:
uses: arcadible/workflows/.github/workflows/release.yml@v1
```
It runs three jobs, on Node.js 24:
| Job | Does |
| --- | --- |
| `setup` | Installs the CLI, runs [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate), and checks that the tag matches `version` |
| `build` | Installs dependencies from the lockfile when there's a build, then runs [`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) and [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack) |
| `publish` | Creates a GitHub Release named after the repository and version, with the zip attached, which Arcadible publishes |
- **Keep `contents: write`.** The `publish` job needs it to create the GitHub Release.
- **Commit a lockfile** if the project has a `package.json`. [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) requires one, and the workflow installs and caches dependencies by it.
## The check on each release
Arcadible adds a check named **Arcadible** to the tagged commit, next to the workflow's own checks. It passes with `Version 1.2.0 accepted` once Arcadible has the build, or fails with what happened, the reason, and a link to Arcadible Studio. Each reason, and how to fix it, is in [Release failure reasons](https://developer.arcadible.com/references/errors#release-reasons).
Arcadible also emails the game's owner when the build is published or the release fails, whoever pushed the tag.
## When a release doesn't arrive
| What you see | Why | What to do |
| --- | --- | --- |
| GitHub emails that the workflow run failed | A job failed, often [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) or the build. Arcadible never receives the release. | Open the run on the repository's Actions tab, fix the cause, delete the tag as below, and release again. |
| [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) times out, and the Actions run is still going | The build takes longer than 15 minutes. | Wait for the run, or pass a longer `--timeout`. |
| The Arcadible check fails | Arcadible refused the release. | Read the check's reason, and see [Release failure reasons](https://developer.arcadible.com/references/errors#release-reasons). |
| The check says `This GitHub account isn't connected to Arcadible.` | The App is installed on GitHub, but no Arcadible account has connected the installation. | In Arcadible Studio's **Settings ▸ Integrations**, choose **Connect an existing installation**, as in [Set it up](https://developer.arcadible.com/publish/deploying-from-github#set-it-up). |
`arc deploy git` refuses a version whose tag already exists. After a failed run, nothing was published, so delete the tag locally and on `origin` (`git push origin :refs/tags/arcadible/release/v1.2.0`) and release the same version again, or release a higher one.
## Releasing as a team
Anyone who can push tags to the repository can release the game, and builds are published to the game's owner's account. Whoever releases, logged in to Arcadible or not, [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) checks the repository first and waits until the release is published or fails, and says why. The **Arcadible** check on the tagged commit says the same, and the owner gets the email either way. To limit who can release, add a [tag ruleset](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets) on GitHub that restricts creating tags matching `arcadible/release/*`.
## Other CI providers
[`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) needs a person to approve it, so other CI providers can't currently deploy. Release from GitHub Actions with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git).
## Next steps
- [Releases](https://developer.arcadible.com/publish/releases): where you hear about a release, and how to fix a failed one.
- [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git): every check `arc deploy git` runs, and its output.
- [FAQ](https://developer.arcadible.com/faq#deploying-and-releases): common questions about deploying.
# Going Public on Arcadible
A game is unlisted until you make it public: anyone with the link can play it, but it isn't in the store or in search. This page is the checklist for before you change that, and what changes after.
## Before you go public
**The game**
- [ ] The release you'll make public was tested on staging, in a browser and in the Arcadible desktop app ([Testing Your Game](https://developer.arcadible.com/build/testing))
- [ ] It's tested on a phone, if [`inputs`](https://developer.arcadible.com/references/arcadible-manifest#game.inputs) includes `touch`
- [ ] If it has progress, it keeps it across an update ([Saves](https://developer.arcadible.com/build/saves)), and it pauses when hidden ([Best Practices](https://developer.arcadible.com/build/best-practices#pause-save-and-resume))
- [ ] It loads quickly on a mobile connection ([Load fast](https://developer.arcadible.com/build/best-practices#load-fast))
**The store page**
- [ ] Every piece of art in `.arcadible/` is the game's own, not the starter's ([Your Store Page](https://developer.arcadible.com/publish/store-page))
- [ ] The README's title, tagline, bio, and description describe this game
- [ ] A [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle), if you want links to read as the game's name
- [ ] [`genres`](https://developer.arcadible.com/references/arcadible-manifest#game.genres), [`inputs`](https://developer.arcadible.com/references/arcadible-manifest#game.inputs), and [`orientation`](https://developer.arcadible.com/references/arcadible-manifest#game.orientation) match the game
- [ ] [`audience`](https://developer.arcadible.com/references/arcadible-manifest#game.audience) and [`ai`](https://developer.arcadible.com/references/arcadible-manifest#game.ai) are set, so the store shows the right notices
- [ ] [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) finds no issues
## Make it public
Set the visibility, and deploy a release version:
```toml title="arcadible.toml"
[game]
version = "1.0.0"
visibility = "public"
```
```sh
arc deploy
```
**It must be a release version.** A prerelease goes to staging and never changes the store page, so it can't make a game public. See [Visibility](https://developer.arcadible.com/concepts#visibility).
Until the game is public, its **Overview** in [Arcadible Studio](https://developer.arcadible.com/studio#overview) lists **Make it public** under **Next steps**.
## What changes
A public game is listed wherever games are:
- **The store:** search, and the lists and genres it shows.
- **Your developer page,** `arcadible.com/@`, which lists your public games. It's unlisted until you make your profile public in **Settings ▸ Profile**: a public game doesn't make it public.
- **"More by",** on the store pages of your other games.
Its links don't change, and neither does anything for players who already have them.
## After
- **Watch it in Studio.** Each game's **Overview** shows its plays and likes. See [Arcadible Studio](https://developer.arcadible.com/studio).
- **Ship updates the same way:** bump the version, try a prerelease on staging, then release. See [Releases](https://developer.arcadible.com/publish/releases).
- **To unlist it,** set `visibility = "unlisted"` and deploy a release. The game leaves the store's lists and search, and its links keep working.
# Arcadible Studio
[Arcadible Studio](https://studio.arcadible.com) is where you see your games once they're on Arcadible: their plays and likes, their builds and releases, and what their store pages are made of. You log in with your Arcadible account.

Studio shows what your project made. It doesn't edit games: a game is created by its first deploy, and its store page changes when you deploy. So there are no forms to keep in step with your repository.
Studio has three pages: **Games**, **Builds**, and **Settings**.
## Games
**Games** lists every game on your account, public and unlisted, with its **Visibility**, **Plays**, and when it was last **Updated**. While one of your games is public and your own profile is unlisted, a note above them says players can't find you or your other games from it, with **Make it public**, which opens your profile's settings.
A game with no production release shows **Not released** in place of its visibility, and is named and pictured by its newest build, since the game's own title and icon come from its first production release.
**New game** shows the commands that create one with the CLI. A game appears in the list once its first build is published.
## A game
Open a game for its header, with **View in store** for its store page and **Play** for its play page, and four tabs. For a game not released yet, the header shows **Not released**, and **Play** opens its newest prerelease.
### Overview
- **Next steps,** until the game is released and public: **Release it to production**, which links [Releases](https://developer.arcadible.com/publish/releases); **Make it public**, which links [Going Public](https://developer.arcadible.com/publish/going-public); and **Release from GitHub (optional)**, which opens the **Repository** tab. Each step that's left shows what does it, such as [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) or `visibility = "public"`, and done steps are checked. The list goes once the first two are done.
- **Plays**, counted from production builds, and **Likes**.
- **In production:** the version players get, with its channel and when it was deployed.
- **Latest staging build:** the newest prerelease.
- **Delete game.** See [Managing a Game](https://developer.arcadible.com/studio/games#delete-a-game).
Each version links to its build, and **Play** opens it.
### Builds
Above the builds, a line for each release on its way, with its status (**Pending** or **Processing**) and version, and one line, **Releases to fix: N**, for the failures since the game's last published release. **Show why** opens the newest five, with **See all under Failed** when there are more. Each failure shows its version and channel, where it came from (**arc deploy** or **GitHub**), what happened, its **Reason**, and **How to fix**, which links to its [reason](https://developer.arcadible.com/references/errors#release-reasons)'s entry.
Below them, every build of the game, newest first.
### Store
The store page as players see it, rendered by the store's own page from a build's values. Choose **Production** or **Staging**, each with its version: the newest build in that channel, and disabled without one.
- **Production** is what players see.
- **Staging** is what they'll see once that version is released to production. Before a game's first production release, the tab opens on **Staging**, and says that until then players see only "Not released yet".
**Play** with the version, such as **Play 1.2.0**, opens that build. The preview's screenshots and description images open full screen.
Below the preview:
- **Details:** that build's title, short title, tagline, bio, visibility, genres, controls, and orientation, and the game's handle.
- **Artwork and gallery:** the artwork, and every gallery file by its file name, each opening full screen.
With no builds, the tab shows **No builds yet**. It's read-only: to change the store page, edit your project and deploy a release version. **Your Store Page** links to [Your Store Page](https://developer.arcadible.com/publish/store-page).
### Repository
The GitHub repository the game releases from, with **Disconnect repository**. With none, it lists the repositories your GitHub installations can access, so you can **Connect** one; with no installation yet, **Go to Integrations** opens Settings to install the app. A repository without the release workflow shows **No release workflow**. See [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github).
### Not published yet
A game whose first release failed doesn't exist yet, so its link shows **Not published yet** with the failed releases. Fix the cause and deploy again.
## Builds
**Builds** lists the builds of all your games, with tabs for **All**, **Production**, **Staging**, and **Failed**. **Failed** keeps every release that failed, with its reason. Above the builds are the same lines as on a game's **Builds** tab, for all your games: one for each release on its way, and **Releases to fix: N**.
Open a build for its **Game**, **Version**, **Channel**, **Size**, when it was **Deployed**, its **Origin** (a link to its files, outside the play page), and its **Manifest**: the `arcadible.json` it was deployed with. **Play this build** opens its version link.
## Settings
- **Integrations:** the Arcadible GitHub App. **Install GitHub App** installs it on a GitHub account or organization, and **Connect an existing installation** connects one installed earlier. Each installation has **Manage on GitHub** and **Disconnect**. Disconnecting stops releases from its repositories; the app stays installed on GitHub. Uninstalling the app on GitHub disconnects its games' repositories too: after installing it again, connect each one again on its game's **Repository** tab. See [Set it up](https://developer.arcadible.com/publish/deploying-from-github#set-it-up).
- **Profile** opens your settings on [arcadible.com](https://arcadible.com), which Studio shares: your name, handle, profile picture, background, poster, tagline, bio, and homepage, shown on your developer page, and whether your profile is public.
## Studio's words and the docs'
Studio names some things differently from these docs:
| Studio | The docs |
| --- | --- |
| **Builds** (the page) | Builds, and the releases still on their way or failed |
| **arc deploy** | A direct deploy |
| **GitHub** | A `git` deploy |
| **In production** | The latest build in the `production` channel |
| **Latest staging build** | The latest build in the `staging` channel |
| **Store** | The store page |
See [How Arcadible Works](https://developer.arcadible.com/concepts) for each term.
# Managing a Game
What you can change about a game after it's on Arcadible, and where. Most of it is in your project, not in Studio.
## Ownership
A game belongs to the account that first deploys its [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id). Only that account can deploy it directly, see it in Studio, and delete it. A game can't currently be transferred to another account, and accounts don't have teams.
**To release as a team,** connect the game's repository: anyone who can push to it can release with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git), and builds are published to the owner's account. See [Releasing as a team](https://developer.arcadible.com/publish/deploying-from-github#releasing-as-a-team).
## Visibility
Visibility is set in the manifest, not in Studio: set [`visibility`](https://developer.arcadible.com/references/arcadible-manifest#game.visibility) and deploy a release version. A game is unlisted until then, playable by link but not listed. See [Visibility](https://developer.arcadible.com/concepts#visibility), and [Going Public](https://developer.arcadible.com/publish/going-public).
## The handle
The [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle) changes with a release version: your first deploy claims it, and after that only a release can change it. The old handle stops working in links, and is held for your account for 30 days before another account's game can take it, so update anywhere you've shared it. Until the game's first production release, it keeps a handle for 90 days ([Limits › Handles](https://developer.arcadible.com/references/limits#handles)). The `id` link, `play.arcadible.com//`, never changes.
## Several games in one repository
Give each game its own directory, with its own `arcadible.toml`, and deploy each with `-C`:
```sh
arc deploy -C games/star-drift
arc deploy -C games/comet
```
[`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) releases only a game at the repository's root, since the workflow builds from there, so release games in directories directly.
## Delete a game
In [Arcadible Studio](https://studio.arcadible.com), open the game's **Overview** and choose **Delete game**.
> **Deleting a game is permanent.** The game and all of its builds are removed from Arcadible at once, and it can't be undone.
- **Its handle is held** for your account for 30 days: another account's game can't take it until then.
- **Its `id` can't be deployed again.** Deploying it fails with `Game unavailable`. To publish the project as a new game, give it a new `id`: delete the `id` line from `arcadible.toml` and run [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init).
Players lose access to the game and its builds, including any links they've saved.
# Arcadible Manifest
The manifest describes your game: its identity, its store page, and how to build it. Most of its values are computed from files your project already has, so the store page is versioned and reviewed alongside your code.

A typical `arcadible.toml` is short:
```toml title="arcadible.toml"
[game]
id = "3f0c9a52-8d1e-4f7b-9a26-5b1c0e7d4a18"
version = "0.1.0"
handle = "star-drift"
genres = ["arcade", "action"]
audience = "everyone"
ai = "unused"
orientation = "landscape"
inputs = ["keyboard", "gamepad"]
[workflow]
build = "./build.sh"
deploy_dir = "build"
```
Store text comes from your README, and art from the `.arcadible/` directory. You can override either here; see [Sources](https://developer.arcadible.com/references/arcadible-manifest#sources).
## Sources
The CLI builds the manifest by merging these sources, in order. A field set in a later source overrides the same field from an earlier one.
| # | Source | Provides |
| --- | --- | --- |
| 1 | `arcadible.md`, or `README.md` if there's no `arcadible.md` | [`title`](https://developer.arcadible.com/references/arcadible-manifest#game.title), [`short_title`](https://developer.arcadible.com/references/arcadible-manifest#game.short_title), [`tagline`](https://developer.arcadible.com/references/arcadible-manifest#game.tagline), [`bio`](https://developer.arcadible.com/references/arcadible-manifest#game.bio), [`description`](https://developer.arcadible.com/references/arcadible-manifest#game.description) |
| 2 | `.arcadible/` | [`icon`](https://developer.arcadible.com/references/arcadible-manifest#game.icon), [`cover`](https://developer.arcadible.com/references/arcadible-manifest#game.cover), [`banner`](https://developer.arcadible.com/references/arcadible-manifest#game.banner), [`poster`](https://developer.arcadible.com/references/arcadible-manifest#game.poster), [`splash`](https://developer.arcadible.com/references/arcadible-manifest#game.splash), their `_video` variants, and [`gallery`](https://developer.arcadible.com/references/arcadible-manifest#game.gallery) |
| 3 | `package.json`, if present | [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version) and [`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) |
| 4 | `arcadible.toml` | Everything else, and an override for anything above |
If your project uses a `package.json`, the CLI reads its `version`, and builds with its `build` script, so you don't have to keep a second version number in `arcadible.toml`. Projects without a `package.json` don't need to add one.
To leave an optional field unset, leave it out. An empty value, such as `tagline = ""` or `genres = []`, fails validation. The exception is [`build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build), where `build = ""` or `build = []` means there's no build step, even if `package.json` has one.
Arcadible ignores fields and tables it doesn't know, such as a misspelled `genre`: they're left out of what [`arc inspect`](https://developer.arcadible.com/references/arcadible-cli/arc-inspect) prints and what's deployed. [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate#warnings) warns about each, with the name it might mean when one is close, without failing.
To see the result, run [`arc inspect`](https://developer.arcadible.com/references/arcadible-cli/arc-inspect). It prints the fully resolved manifest, which is exactly what [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) checks and what gets deployed.
```sh
arc inspect # the whole manifest, as TOML
arc inspect game.title # a single value
arc inspect --json # machine-readable
arc validate # check it against the rules on this page
```
Only the `game` table is published with the build. The `workflow` table is only used locally and in CI.
## Media files
Media is read from the `.arcadible/` directory. Art uses fixed file names, so you don't need to reference it anywhere:
```text
.arcadible/
├── icon.png icon_video.mp4
├── cover.png cover_video.mp4
├── banner.png banner_video.mp4
├── poster.png poster_video.mp4
├── splash.png splash_video.mp4
└── gallery/
├── screenshot_01.png
├── screenshot_02_phone.png
└── boss-fight.mp4
```
Each piece of art has a fixed aspect ratio and a range of allowed sizes:

| Field | File | Ratio | Dimensions |
| --- | --- | --- | --- |
| [`icon`](https://developer.arcadible.com/references/arcadible-manifest#game.icon) | `icon.png`, `icon_video.mp4` | 1:1 | Minimum: 512 × 512 Maximum: 2048 × 2048 |
| [`cover`](https://developer.arcadible.com/references/arcadible-manifest#game.cover) | `cover.png`, `cover_video.mp4` | 3:2 | Minimum: 768 × 512 Maximum: 3072 × 2048 |
| [`banner`](https://developer.arcadible.com/references/arcadible-manifest#game.banner) | `banner.png`, `banner_video.mp4` | 2:1 | Minimum: 1024 × 512 Maximum: 4096 × 2048 |
| [`poster`](https://developer.arcadible.com/references/arcadible-manifest#game.poster) | `poster.png`, `poster_video.mp4` | 2:3 | Minimum: 512 × 768 Maximum: 2048 × 3072 |
| [`splash`](https://developer.arcadible.com/references/arcadible-manifest#game.splash) | `splash.png`, `splash_video.mp4` | 16:9 | Minimum: 1024 × 576 Maximum: 4096 × 2304 |
| [`gallery`](https://developer.arcadible.com/references/arcadible-manifest#game.gallery) | `gallery/*.png`, `gallery/*.mp4` | Any | Minimum: 512 × 512 Maximum: 2048 × 2048 |
Where each piece may appear in the store, and how to make it read well there, is in [Your Store Page](https://developer.arcadible.com/publish/store-page), with each piece's recommended size and [edge margin](https://developer.arcadible.com/publish/store-page#edge-margin).
These rules apply to every media field:
| Format | File size | Duration | Video track |
| --- | --- | --- | --- |
| PNG | Maximum: 2 MB | — | — |
| MP4 | Maximum: 15 MB | Minimum: 3 seconds Maximum: 30 seconds | Required |
- **Aspect ratios are exact.** 1024 × 512 is 2:1, and 1030 × 512 is rejected.
- **The maximum is four times the minimum** on each side. A banner can be anywhere from 1024 × 512 to 4096 × 2048, as long as it's exactly 2:1.
- **File names are case-sensitive.** Use lowercase names and extensions: `Icon.PNG` isn't detected.
- **Overrides stay inside `.arcadible/`.** Setting a media field in `arcadible.toml` replaces the default file name, but the path must be relative and inside `.arcadible/`.
- **Videos are optional companions.** Each `_video` field is a variant that may play in place of its image, without sound. See [Art videos](https://developer.arcadible.com/references/arcadible-manifest#art-videos).
> **Media is public.** `.arcadible/` is published with the build, so its files can be downloaded from the build's origin. Don't put anything private there.
## `game`
Describes the game and its store page. It's published with every build.
### `id`
The permanent identifier of your game.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | Yes |
| Length | 36 characters |
| Characters | Lowercase letters `a`–`f`, digits, and hyphens |
| Format | UUID ([RFC 9562](https://www.rfc-editor.org/rfc/rfc9562)), such as one from [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) |
**Example**
```toml
[game]
id = "3f0c9a52-8d1e-4f7b-9a26-5b1c0e7d4a18"
```
**Notes**
- [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) generates it. Running `arc init` in an existing project adds one, and a `version` of `0.1.0-beta.1` if neither `arcadible.toml` nor `package.json` has one. When it adds either, it rewrites `arcadible.toml`, so comments and formatting aren't kept.
- Commit it and never change it. If you do, Arcadible treats the project as a different game.
- Once a game is deleted in Arcadible Studio, its `id` can't be deployed again. Deleting is immediate and can't be undone; to publish the project again, give it a new `id`.
### `version`
The version of the build.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml`, or `version` in `package.json` |
| Required | Yes, unless `package.json` has a `version` |
| Format | Normalized [semver](https://semver.org), in lowercase: `1.2.0`, not `v1.2.0`, `1.2`, `1.2.0+build.1`, or `1.2.0-Beta.1` |
**Example**
```toml
[game]
version = "1.2.0"
```
**Notes**
- Each version can be published once. Bump it for every deploy. If a deploy fails before its build is published, you can deploy the same version again.
- A **prerelease** version such as `1.3.0-beta.1` is deployed to the `staging` channel. Any other version goes to `production`. Play a channel with `arc play -r staging`.
### `handle`
A human-readable name used in URLs, such as `play.arcadible.com/star-drift/`.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Length | Minimum: 3 characters Maximum: 24 characters |
| Characters | Lowercase letters, digits, and hyphens |
| Format | Starts and ends with a letter or digit, and doesn't contain `--` |
**Example**
```toml
[game]
handle = "star-drift"
```
**Notes**
- It must be unique among games, and some names are reserved. [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) refuses a handle another game owns, one held for another account, or a reserved one. A release from `arc deploy git` with a taken handle still publishes, and the game keeps the handle it had, or none.
- Your first deploy claims it, even if it's a prerelease. After that, only a release version can change it.
- Until a game's first production release, it keeps its handle for 90 days, and your unreleased games hold at most 5 handles. See [Limits › Handles](https://developer.arcadible.com/references/limits#handles).
- When it changes, links with the old handle stop working, and the old handle is held for your account for 30 days. Links with the game's `id` keep working as long as the game exists.
- Deleting a game in Arcadible Studio holds its handle for your account for 30 days.
- Without a handle, the game is addressed by its `id`.
### `title`
The game's name.
| Rule | Value |
| --- | --- |
| Source | The first `#` heading of the README |
| Required | No |
| Length | Maximum: 30 characters |
| Characters | Letters, digits `0`–`9`, spaces, and `. , ! ? ' " ( ) : ; -` |
| Format | Plain text |
**Example**
```md
# Star Drift
```
**Notes**
- The character set excludes curly quotes, `&`, `/`, and emoji.
- Accented letters must be precomposed ([NFC](https://unicode.org/reports/tr15/)). A letter followed by a combining accent is rejected.
### `short_title`
A shorter name for places where the full title doesn't fit, such as under an icon.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml`, or a parenthesized suffix on the README title |
| Required | No |
| Length | Maximum: 15 characters |
| Characters | Letters, digits `0`–`9`, spaces, and `. , ! ? ' " ( ) : ; -` |
| Format | Plain text |
**Example**
```md
# Star Drift: Beyond the Rim (Star Drift)
```
That heading gives `title = "Star Drift: Beyond the Rim"` and `short_title = "Star Drift"`.
**Notes**
- When `short_title` is set in `arcadible.toml`, the heading is read as the title word for word, so a title can end in parentheses of its own:
```toml
[game]
short_title = "Star Drift"
```
With that, `# Star Drift (Deluxe)` gives `title = "Star Drift (Deluxe)"`.
### `tagline`
A one-line hook.
| Rule | Value |
| --- | --- |
| Source | The README's `##` heading right after its `#` title |
| Required | No |
| Length | Maximum: 30 characters |
| Characters | Letters, digits `0`–`9`, spaces, and `. , ! ? ' " ( ) : ; -` |
| Format | Plain text |
**Example**
```md
# Star Drift
## Drift. Dodge. Survive.
```
### `bio`
The game's pitch. It's shown in About when there's no description, and may appear, cut to two lines, where the game is featured.
| Rule | Value |
| --- | --- |
| Source | The paragraph directly after the tagline in the README |
| Required | No |
| Length | Maximum: 180 characters |
| Characters | Letters, digits `0`–`9`, spaces, and `. , ! ? ' " ( ) : ; -` |
| Format | Plain text |
**Notes**
- Formatting is ignored, so write it as plain text.
- Put nothing between the tagline and the bio, or the README has no bio. See [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown#structure).
### `description`
The long-form About section on the store page.
| Rule | Value |
| --- | --- |
| Source | The README content between the **first and last** `---` |
| Required | No |
| Length | Maximum: 3,000 characters |
| Characters | Printable text and line breaks, without tabs or zero-width joiners |
| Format | [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown), limited to the [allowed elements](https://developer.arcadible.com/references/arcadible-markdown#elements) |
**Notes**
- If the README has anything after its title, tagline, and bio, it must have at least two `---` rules. See [Structure](https://developer.arcadible.com/references/arcadible-markdown#structure).
- The length is measured on the **rendered HTML**, so tags and URLs count too.
- Images and videos must come from [`.arcadible/gallery/`](https://developer.arcadible.com/references/arcadible-manifest#game.gallery).
- Setting `description` in `arcadible.toml` means writing HTML by hand, so use the README.
### `genres`
The game's genres, used to group it in the store.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Count | Maximum: 3 |
**Values**
| Value | Shown as |
| --- | --- |
| `action` | Action |
| `adventure` | Adventure |
| `arcade` | Arcade |
| `board` | Board |
| `card` | Card |
| `casino` | Casino |
| `casual` | Casual |
| `music` | Music |
| `puzzle` | Puzzle |
| `racing` | Racing |
| `rpg` | RPG |
| `simulation` | Simulation |
| `sports` | Sports |
| `strategy` | Strategy |
| `trivia` | Trivia |
| `word` | Word |
**Example**
```toml
[game]
genres = ["puzzle", "casual"]
```
**Notes**
- Each genre can appear only once.
### `audience`
Who the game's content is suitable for. It's shown to players on the store page.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Default | `everyone` |
**Values**
| Value | Label | Description |
| --- | --- | --- |
| `everyone` | Everyone | May contain minimal cartoon, fantasy or mild violence and/or infrequent use of mild language. |
| `mature` | Mature | May contain intense violence, blood and gore, sexual content and/or strong language. |
For how to choose, see [Choosing an audience](https://developer.arcadible.com/publish/store-page#audience).
**Example**
```toml
[game]
audience = "mature"
```
**Notes**
- The audience is part of every deploy. If an update adds more intense content, change the audience in the same release.
### `ai`
Discloses whether the game's content was made with generative AI. When it's `generative`, the store page shows a label telling players so. `unused` shows no label.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Default | None, and the game's AI use is recorded as `unknown` |
**Values**
| Value | Label | Description |
| --- | --- | --- |
| `unused` | No label | The game's content wasn't generated by AI. |
| `generative` | Generative | May contain content generated in whole or in part by AI, including during gameplay. |
For how to choose, see [Disclosing AI](https://developer.arcadible.com/publish/store-page#ai).
**Example**
```toml
[game]
ai = "generative"
```
**Notes**
- If you leave `ai` out, no label is shown, but your game's AI use is recorded as `unknown` rather than `unused`. Set it explicitly so it reflects your answer.
- The disclosure is part of every deploy. If an update adds generated content, change it in the same release.
### `orientation`
The screen orientation the game expects.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Default | `natural` |
**Values**
| Value | Meaning |
| --- | --- |
| `natural` | The device's natural orientation |
| `any` | Any orientation, with rotation allowed |
| `portrait`, `landscape` | Either direction of that orientation |
| `portrait-primary`, `portrait-secondary`, `landscape-primary`, `landscape-secondary` | A single, fixed direction |
**Example**
```toml
[game]
orientation = "landscape"
```
**Notes**
- The Arcadible desktop app uses it to shape its window.
- The values mirror the [Screen Orientation API](https://developer.mozilla.org/docs/Web/API/ScreenOrientation/type).
### `inputs`
The input methods the game supports.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Default | None |
**Values**
| Value | Meaning |
| --- | --- |
| `keyboard` | Keyboard only |
| `mouse` | Mouse only |
| `keyboard-mouse` | Keyboard and mouse together |
| `gamepad` | Gamepad |
| `gamepad-motion` | Gamepad with motion controls |
| `touch` | Touchscreen |
| `touch-motion` | Touchscreen with device motion |
| `motion` | Device motion only |
For how to choose, see [Declaring inputs](https://developer.arcadible.com/publish/store-page#inputs).
**Example**
```toml
[game]
inputs = ["keyboard", "gamepad"]
```
**Notes**
- Each input can appear only once.
- If you leave `inputs` out, the store page doesn't list your game's inputs. Set it so players know how to play.
### `visibility`
Whether the game is listed in the store and in search.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Default | `unlisted` |
**Values**
| Value | Meaning |
| --- | --- |
| `unlisted` | Anyone with the link can play the game, but it isn't listed in the store or in search |
| `public` | The game is listed |
**Example**
```toml
[game]
visibility = "public"
```
**Notes**
- Like the rest of the store page, it changes only with a release version. See [Visibility](https://developer.arcadible.com/concepts#visibility).
### `trailer`
A link to the game's trailer video.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Length | Maximum: 255 characters |
| Format | An `https` YouTube or Vimeo URL |
**Example**
```toml
[game]
trailer = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
```
**Notes**
- These forms are accepted: `youtube.com/watch?v=…`, `youtube.com/embed/…`, `youtu.be/…`, `vimeo.com/`, and `player.vimeo.com/video/`.
### `icon`
The game's icon. It may appear as the app icon, in lists and the library, and on the store page.
| Rule | Value |
| --- | --- |
| Source | `.arcadible/icon.png` |
| Required | No |
| Format | PNG |
| Aspect ratio | 1:1 |
| Dimensions | Minimum: 512 × 512 Maximum: 2048 × 2048 |
| File size | Maximum: 2 MB |
**Notes**
- For text in art, see [Text in art](https://developer.arcadible.com/publish/store-page#text-in-art).
### `cover`
Landscape art. It may appear in search results, and in link previews when the game is shared.
| Rule | Value |
| --- | --- |
| Source | `.arcadible/cover.png` |
| Required | No |
| Format | PNG |
| Aspect ratio | 3:2 |
| Dimensions | Minimum: 768 × 512 Maximum: 3072 × 2048 |
| File size | Maximum: 2 MB |
**Notes**
- For text in art, see [Text in art](https://developer.arcadible.com/publish/store-page#text-in-art).
### `banner`
Wide art. It may appear in featured spots and in wide headers, such as on phones.
| Rule | Value |
| --- | --- |
| Source | `.arcadible/banner.png` |
| Required | No |
| Format | PNG |
| Aspect ratio | 2:1 |
| Dimensions | Minimum: 1024 × 512 Maximum: 4096 × 2048 |
| File size | Maximum: 2 MB |
**Notes**
- For text in art, see [Text in art](https://developer.arcadible.com/publish/store-page#text-in-art).
### `poster`
Tall art. It may appear on tall cards and in headers.
| Rule | Value |
| --- | --- |
| Source | `.arcadible/poster.png` |
| Required | No |
| Format | PNG |
| Aspect ratio | 2:3 |
| Dimensions | Minimum: 512 × 768 Maximum: 2048 × 3072 |
| File size | Maximum: 2 MB |
**Notes**
- For text in art, see [Text in art](https://developer.arcadible.com/publish/store-page#text-in-art).
### `splash`
Background art. It may appear behind the game's title and text, blurred or tinted.
| Rule | Value |
| --- | --- |
| Source | `.arcadible/splash.png` |
| Required | No |
| Format | PNG |
| Aspect ratio | 16:9 |
| Dimensions | Minimum: 1024 × 576 Maximum: 4096 × 2304 |
| File size | Maximum: 2 MB |
**Notes**
- For text in art, see [Text in art](https://developer.arcadible.com/publish/store-page#text-in-art).
### Art videos
Each piece of art can have a video variant, which may play in its place, without sound. Provide the PNG too, for places that don't show video.
| Rule | Value |
| --- | --- |
| Required | No |
| Format | MP4 |
| Aspect ratio | The same as its art |
| Dimensions | The same as its art |
| File size | Maximum: 15 MB |
| Duration | Minimum: 3 seconds Maximum: 30 seconds |
| Video track | Required |
### `icon_video`
A video of [`icon`](https://developer.arcadible.com/references/arcadible-manifest#game.icon), from `.arcadible/icon_video.mp4`.
### `cover_video`
A video of [`cover`](https://developer.arcadible.com/references/arcadible-manifest#game.cover), from `.arcadible/cover_video.mp4`.
### `banner_video`
A video of [`banner`](https://developer.arcadible.com/references/arcadible-manifest#game.banner), from `.arcadible/banner_video.mp4`.
### `poster_video`
A video of [`poster`](https://developer.arcadible.com/references/arcadible-manifest#game.poster), from `.arcadible/poster_video.mp4`.
### `splash_video`
A video of [`splash`](https://developer.arcadible.com/references/arcadible-manifest#game.splash), from `.arcadible/splash_video.mp4`.
### `gallery`
Every PNG and MP4 in `.arcadible/gallery/`, sorted by file name. The gallery serves two purposes:
- **Screenshots** for the store page carousel, when they're named with the [screenshot convention](https://developer.arcadible.com/references/arcadible-manifest#screenshots).
- **Media to embed** in the [`description`](https://developer.arcadible.com/references/arcadible-manifest#game.description), using Markdown image syntax.
| Rule | Value |
| --- | --- |
| Source | `.arcadible/gallery/` |
| Required | No |
| Count | Maximum: 24 files |
**Images**
| Rule | Value |
| --- | --- |
| Source | `.arcadible/gallery/*.png` |
| Required | No |
| Format | PNG |
| Aspect ratio | Any |
| Dimensions | Minimum: 512 × 512 Maximum: 2048 × 2048 |
| File size | Maximum: 2 MB |
**Videos**
| Rule | Value |
| --- | --- |
| Source | `.arcadible/gallery/*.mp4` |
| Required | No |
| Format | MP4 |
| Aspect ratio | Any |
| Dimensions | Minimum: 512 × 512 Maximum: 2048 × 2048 |
| File size | Maximum: 15 MB |
| Duration | Minimum: 3 seconds Maximum: 30 seconds |
| Video track | Required |
**Notes**
- **Images and videos count together** toward the 24-file limit.
- **File names must be unique once the extension is removed.** `screenshot_01.png` and `screenshot_01.mp4` can't both exist.
- **Extensions must be lowercase:** `.png` and `.mp4`. Files with any other extension, including `.PNG` and `.MP4`, are skipped by the directory scan.
- **A screenshot embedded in the description is shown twice,** in the carousel and in the description. Give media that's only for the description another name, such as `boss-fight.png`.
- Setting `gallery` in `arcadible.toml` replaces the directory scan. Any description media that's missing from the list is removed from the published page.
### Screenshots
Files named `screenshot_[_].` are shown in the store page carousel, ordered by number. Other gallery files are never shown in the carousel.
```text
.arcadible/gallery/
├── screenshot_01.png # all platforms
├── screenshot_02.mp4 # all platforms
├── screenshot_01_phone.png # phones
└── screenshot_02_phone.png # phones
```
`platform` is `desktop`, `phone`, or `tv`, and it's matched against the viewer's device:
- If any screenshot is qualified for the viewer's platform, only the qualified ones are shown.
- Otherwise, the unqualified screenshots are shown.
- Screenshots qualified for a different platform are never shown.
In the example, phone viewers see `screenshot_01_phone.png` and `screenshot_02_phone.png`. Everyone else sees `screenshot_01.png` and `screenshot_02.mp4`.
## `workflow`
Describes how to turn the repository into a deployable directory. It's used by [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve), and the GitHub release workflow, and it isn't part of the published manifest.
### `build`
A shell command, or a list of commands joined with `&&`, run from the project root.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml`, or `npm run build` when `package.json` has a `build` script |
| Required | No |
| Default | `""`, so no build runs |
**Example**
```toml
[workflow]
build = "npm run build"
```
**Notes**
- Without a `build` in `arcadible.toml`, a `package.json` with a `build` script builds with `npm run build`, as you would locally: its tools in `node_modules` are found, and `prebuild` and `postbuild` run. It works for pnpm and Yarn projects too. To skip that script, set `build = ""`.
- A list is useful for multiple steps:
```toml
[workflow]
build = ["./fetch-assets.sh", "./build.sh"]
```
- The command must produce [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir).
### `deploy_dir`
The directory that gets deployed.
| Rule | Value |
| --- | --- |
| Source | `arcadible.toml` |
| Required | No |
| Default | `.arcadible-public` |
| Format | A relative path to a directory inside the project, such as `dist`, but not the project root, and not inside `.arcadible/` |
| Contents | An `index.html` at its root, which is the entry point players load |
| Size | Maximum: 64 MB, compressed |
**Example**
```toml
[workflow]
deploy_dir = "dist"
```
**Notes**
- It must already exist unless a [`build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) step creates it.
- Its root can't contain `arcadible.json` or `.arcadible`. They're reserved for the files Arcadible adds when it packages the game.
- Its root can't contain an `arcadible` folder either, in any letter case. Every game's address keeps `/arcadible/` for Arcadible's own files.
- The packed game, meaning `deploy_dir` and `.arcadible/`, can be up to 256 MB uncompressed, in at most 10,000 files of up to 64 MB each.
# Arcadible Markdown
Your store page is your README. Arcadible reads the game's title, tagline, bio, and description from Markdown in your repository, so the store page is versioned, reviewed, and deployed with the code. It also renders normally on GitHub.

Arcadible Markdown is ordinary [GitHub Flavored Markdown](https://github.github.com/gfm/) with two differences:
- A fixed **document structure** that maps sections to [manifest](https://developer.arcadible.com/references/arcadible-manifest) fields.
- A restricted **element set** for the description, so every store page renders safely and consistently.
## The file
The CLI reads the first of these files that exists:
1. `arcadible.md`
2. `README.md`
`arcadible.md` takes precedence over `README.md`, so a repository's README can stay about development while `arcadible.md` holds the store page.
## Structure
```md title="README.md"
# Star Drift: Beyond the Rim (Star Drift)

## Drift. Dodge. Survive.
A fast, one-thumb arcade game about surfing the gravity wells of a dying star.
---
Pilot a derelict freighter through collapsing orbits, **chaining slingshots** to outrun the shockwave.

### Features
- 60 hand-built sectors
- Daily seeded runs with global leaderboards
- Full gamepad support
---
## Development
Build instructions for contributors.
```
It maps to the manifest like this:
| Markdown | Field | Rules |
| --- | --- | --- |
| First `#` heading | [`title`](https://developer.arcadible.com/references/arcadible-manifest#game.title) | Up to 30 characters, plain text |
| `(…)` at the end of that heading | [`short_title`](https://developer.arcadible.com/references/arcadible-manifest#game.short_title) | Up to 15 characters, plain text. Not read when `short_title` is set in `arcadible.toml` |
| `##` heading right after the title | [`tagline`](https://developer.arcadible.com/references/arcadible-manifest#game.tagline) | Up to 30 characters, plain text |
| Paragraph directly after the tagline | [`bio`](https://developer.arcadible.com/references/arcadible-manifest#game.bio) | Up to 180 characters, plain text |
| Between the first and last `---` | [`description`](https://developer.arcadible.com/references/arcadible-manifest#game.description) | [Allowed elements](https://developer.arcadible.com/references/arcadible-markdown#elements), up to 3,000 characters of HTML |
| After the last `---` | Ignored | Not read |
Rules to note:
- **The tagline comes right after the title.** Only images, such as a banner or badges, can sit between the `#` title and the `##` tagline, and nothing between the tagline and the bio. A `##` heading anywhere else is a section of your README, not the tagline.
- **The description needs two rules.** If a README with a `#` title has anything after its title, tagline, and bio, it must have at least two `---` lines, or [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) fails ([`description_rules_missing`](https://developer.arcadible.com/references/errors#description_rules_missing)). A README with only a title, tagline, and bio has no description.
- **Leave a blank line above each `---`.** Directly under a line of text, `---` turns that line into a `##` heading instead of a rule.
- **The last `---` ends the description.** You can use `---` inside the description as a divider, and everything after the final one isn't read, so it can hold build notes, badges, or contributor docs.
- **Everything else before the first `---` is ignored.** The banner image in the example is only there for GitHub. The store uses `.arcadible/banner.png` directly.
- **Without a `#` title before the first `---`, there's no title, tagline, or bio,** and `arc validate` doesn't check the structure. A description between two `---` rules is still read. To keep a repository README as it is, put the store page in `arcadible.md`, or set the fields in `arcadible.toml`.
- **The title, tagline, and bio are plain text.** They allow letters, ASCII digits, spaces, and `. , ! ? ' " ( ) : ; -`. Use straight quotes. Emoji, `&`, and `/` are rejected.
Any field can be overridden in `arcadible.toml`, which takes precedence. Run `arc inspect game.description` to see what the CLI extracted.
## Elements
The description is converted to HTML and checked against an allowlist. Anything outside the allowlist fails [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) and isn't published.
| Markdown | Renders as | Notes |
| --- | --- | --- |
| Paragraphs | `p` | |
| `### Heading` | `h3` | The highest heading level available |
| `#### Heading` | `h4` | |
| `**bold**` | `strong` | |
| `*italic*` | `em` | |
| `[text](https://…)`, `[text](url "title")` | `a` | [Link rules](https://developer.arcadible.com/references/arcadible-markdown#links) |
| `- item`, `* item` | `ul` / `li` | Nesting is supported |
| `1. item` | `ol` / `li` | Must start at 1 |
| `> quote` | `blockquote` | |
| `---` | `hr` | Only between the outer rules |
| `` | `img` | [Media rules](https://developer.arcadible.com/references/arcadible-markdown#media) |
| `` | `video` | [Media rules](https://developer.arcadible.com/references/arcadible-markdown#media) |
These aren't supported:
- `#` and `##` headings (they're reserved for the title and tagline), and `#####` or deeper
- Inline code and code blocks
- Strikethrough (`~~`)
- Tables and task lists
- Hard line breaks (a trailing double space or `\`)
- HTML comments (``)
- Raw HTML other than the elements above (raw HTML is checked against the same allowlist)
### Links
- Only `https://` URLs are allowed.
- Relative URLs, `#fragment` links, `http:`, `mailto:`, `javascript:`, and other schemes are rejected. A relative link that works on GitHub, such as `docs/controls.md`, has nothing to point to on the store page.
- Bare `https://` URLs are autolinked, following GFM. A bare `www.` address or email address becomes an `http:` or `mailto:` link, which is rejected.
- Links open in a new tab.
### Media
Images and videos use Markdown image syntax. The file extension decides which element is rendered:
```md
 
```
Markdown has no video syntax, so Arcadible treats an image with an `.mp4` extension as a video. It renders with player controls and a poster frame.
- The `src` must be the file's path in `.arcadible/gallery/`, written as `.arcadible/gallery/`, and the file must exist. Remote URLs and other spellings of the path, such as `./.arcadible/gallery/`, aren't allowed.
- The file must meet the [gallery rules](https://developer.arcadible.com/references/arcadible-manifest#game.gallery): PNG or MP4, from 512 × 512 up to 2048 × 2048, and a unique name without its extension.
- Media is resized for display, up to 1280 pixels wide. Landscape media keeps its aspect ratio. Portrait and square media are cropped to 16:9, so keep the subject centered.
- An image keeps its `alt` text, so write it for screen readers. A video has no `alt`, so the CLI leaves it out; give a video a title instead, such as `"Sector 12"` above.
Any gallery file can be embedded, including [screenshots](https://developer.arcadible.com/references/arcadible-manifest#screenshots). Files that aren't screenshots only appear where you embed them.
# Arcadible SDK
Each build of a game on Arcadible has its own origin, so its [browser storage](https://developer.arcadible.com/build/runtime-limitations#storage-and-saves) starts empty with every update. Saves made through the Arcadible SDK are kept by the play page instead, on the player's device, so they carry over. The SDK is optional, and saves are all it does.
## Install
```sh
npm install @arcadible/sdk
```
Use the package in a game with a build step, such as a Vite project. It also exports `ArcadibleError`, `Codes`, and the TypeScript types.
A game without a build step includes the hosted script instead, which Arcadible serves from every build's own origin, as [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) does locally. Add it as a plain `
```
- **Under another server,** the hosted script's path isn't found, and `window.arcadible` is `undefined`. The package works anywhere.
- **A page has one `arcadible` object:** a game that includes both shares it. The hosted script is always the latest version of `v1`; the package is the version you installed. Both work with every play page.
## Save and load
```js
import { arcadible } from "@arcadible/sdk";
// at startup: once the saves have loaded, the player's progress, or a new game.
await arcadible.ready;
const saved = await arcadible.saves.read("progress");
const progress = saved ? JSON.parse(saved) : { level: 1 };
// at a checkpoint:
await arcadible.saves.write("progress", JSON.stringify(progress));
```
A save is a name holding one string. `arcadible.ready` settles once the game's saves have loaded, and never fails; calls made before it wait for it, and from then on `readSync`, `listSync`, and `infoSync` answer at once from memory, for reads during play. Every call that fails rejects (or, for the sync calls, throws) with an `ArcadibleError`, whose `code` says what to do. `arcadible.saves.where` says who can read the saves back: every version of the game, only this build, or only until the page closes.
Off Arcadible, such as on your dev server, the package works on its own, with the same rules and limits: saves are kept on the page's own origin, `arcadible.saves.where` is `"build"`, and `arcadible.build` is `null`. How to use saves well, across versions and tabs, is in [Saves](https://developer.arcadible.com/build/saves).
## Modules
| Module | Description |
| --- | --- |
| [`arcadible.saves`](https://developer.arcadible.com/references/arcadible-sdk/saves) | Write, read, list, and remove the game's saves, and where they're kept |
## `arcadible`
```ts
interface Arcadible {
readonly ready: Promise<{ build: Build | null }>;
readonly build: Build | null | undefined;
readonly version: string;
readonly saves: Saves;
}
type Build = { version: string; channel: string };
```
| Property | Value |
| --- | --- |
| `arcadible.ready` | Settles once, when the play page has answered and the game's saves have loaded, or after 10 seconds if they haven't. It never rejects, and resolves with `build`, and only that. |
| `arcadible.build` | Which build is running, once `ready` has settled: information, for comparing with a save's version. `null` when nobody says, such as off Arcadible, and `undefined` before `ready`. |
| `arcadible.version` | The SDK's own version, for bug reports. |
| `arcadible.saves` | The game's saves, and [who can read them back](https://developer.arcadible.com/references/arcadible-sdk/saves#saves.where). See [`arcadible.saves`](https://developer.arcadible.com/references/arcadible-sdk/saves). |
```js
const { build } = await arcadible.ready;
// { version: "1.2.0", channel: "production" } on Arcadible, null off it
```
- **`build.version`** is the version you deployed, from `arcadible.toml` or `package.json`.
- **`build.channel`** is `production` or `staging`, and `local` under [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve#sdk). It's a string, and other values may be added.
- **Standing alone,** off Arcadible and outside `arc serve`, `ready` doesn't wait for a play page on a page that isn't in a frame, and waits about a second in one that no play page answers. See [Locally](https://developer.arcadible.com/build/saves#locally).
- **What the SDK logs** starts with `Arcadible SDK:`, as a warning when saves won't be kept as they should (they're late, in memory, or the game stands alone in a frame), and as an error only for a bug.
## Codes
Every call that fails rejects, or throws, with an `ArcadibleError`. Act on its `code`, which the package also exports as `Codes` (`Codes.Full` is `"full"`). Its `message` is for people, and can change.
```ts
class ArcadibleError extends Error {
readonly code: Code; // one of the codes below
}
```
```js
try {
await arcadible.saves.write("progress", JSON.stringify(progress));
} catch (err) {
if (err.code === "full") showStorageFullMessage();
else if (!["invalid", "too_large", "too_many"].includes(err.code)) {
retryLater();
}
}
```
| Code | Means | Retry | What to do |
| --- | --- | --- | --- |
| `invalid` | A name or a save that breaks the [rules](https://developer.arcadible.com/references/limits#saves) | No | Fix the name, or serialize the save to a well-formed string |
| `too_many` | A new save past the most a game can keep | No | Remove a save, or combine saves |
| `too_large` | The game's saves would be larger than they can be together | No | Save less |
| `full` | The device has no room left | No | Tell the player. It works again once they free space |
| `busy` | Too many calls waiting at once | Yes | Wait for some to finish |
| `timeout` | No answer within 10 seconds. A write may still have been kept | Yes | Try again |
| `unavailable` | The saves can't be reached right now | Yes | Try again |
| `not_ready` | A sync call before `ready` | No | Wait for `arcadible.ready` |
| `unsupported` | A call the play page doesn't know | No | Carry on without it |
**New codes may be added.** Treat a code you don't know as `unavailable`, which the example does by retrying any code that isn't final for a write.
# `arcadible.saves`
The game's saves, kept by the play page across versions. Each code a call fails with is in [Codes](https://developer.arcadible.com/references/arcadible-sdk#codes), and the limits are in [Limits](https://developer.arcadible.com/references/limits#saves).
```ts
interface Saves {
write(name: string, data: string): Promise;
read(name: string): Promise;
readSync(name: string): string | null;
list(): Promise;
listSync(): string[];
info(name: string): Promise;
infoSync(name: string): SaveInfo | null;
remove(name: string): Promise;
readonly where: "game" | "build" | "memory" | string | undefined;
}
```
**At startup,** `await arcadible.ready`, then load with `read`, which waits for the saves even when they're late. **During play, read with the sync calls:** `readSync`, `listSync`, and `infoSync` answer at once from the SDK's copy of the saves in memory, and can't fail with `busy` or `timeout`. The async `read`, `list`, and `info` ask the play page each time.
Every call but the sync ones returns a promise, and can be made before `ready`: it waits for it. Names are 1 to 64 letters, digits, `-`, `_`, or `.`. Every call that takes a name rejects, or throws, with `invalid` for any other.
## `write(name, data)`
```ts
write(name: string, data: string): Promise
```
Keeps `data` as the save `name`, replacing any save of that name. The promise resolves with the save's [`SaveInfo`](https://developer.arcadible.com/references/arcadible-sdk/saves#saveinfo) once it's kept on the device.
```js
const info = await arcadible.saves.write("progress", JSON.stringify(progress));
```
- **Fails with** `invalid`, `too_many`, `too_large`, `full`, `busy`, `timeout`, or `unavailable`.
- After `ready`, the save shows in the sync calls at once, in the same tick, before the promise settles. When the play page refuses it, it's taken back.
- `data` is a well-formed string. Serialize anything else, such as with `JSON.stringify`.
- A write that fails with `timeout` may still be kept: the sync calls keep showing it.
## `read(name)`
```ts
read(name: string): Promise
```
Resolves with the save, or `null` when there's none.
```js
const saved = await arcadible.saves.read("progress");
const progress = saved ? JSON.parse(saved) : { level: 1 };
```
- **Fails with** `invalid`, `busy`, `timeout`, or `unavailable`.
- It waits for the saves, even when they're late. When they can't be reached, it fails with `timeout` or `unavailable`, which isn't the same as no save.
## `readSync(name)`
```ts
readSync(name: string): string | null
```
Returns the save, or `null` when there's none, from the SDK's copy in memory, without waiting.
```js
await arcadible.ready;
const settings = JSON.parse(arcadible.saves.readSync("settings") ?? "{}");
```
- **Throws** `not_ready` before `ready`, `unavailable` while the saves haven't loaded, and `invalid` for a bad name.
- The copy includes this page's writes at once, and other tabs' as soon as they're kept.
- If the saves take longer than 10 seconds to load, `ready` settles anyway, and the sync calls throw `unavailable` until they arrive, rather than answering `null` for a save that exists.
## `list()`
```ts
list(): Promise
```
Resolves with the names of the game's saves, sorted.
```js
const slots = (await arcadible.saves.list()).filter((name) => name.startsWith("slot-"));
```
- **Fails with** `busy`, `timeout`, or `unavailable`.
## `listSync()`
```ts
listSync(): string[]
```
Returns the names of the game's saves, sorted, from the SDK's copy in memory, without waiting.
```js
await arcadible.ready;
const slots = arcadible.saves.listSync().filter((name) => name.startsWith("slot-"));
```
- **Throws** `not_ready` before `ready`, and `unavailable` while the saves haven't loaded, as [`readSync`](https://developer.arcadible.com/references/arcadible-sdk/saves#saves.readSync) does.
## `info(name)`
```ts
info(name: string): Promise
```
Resolves with the save's [`SaveInfo`](https://developer.arcadible.com/references/arcadible-sdk/saves#saveinfo), or `null` when there's none.
```js
const info = await arcadible.saves.info("progress");
// { version: "1.2.0", savedAt: "2026-10-12T18:21:24.692Z" }
```
- **Fails with** `invalid`, `busy`, `timeout`, or `unavailable`.
## `infoSync(name)`
```ts
infoSync(name: string): SaveInfo | null
```
Returns the save's [`SaveInfo`](https://developer.arcadible.com/references/arcadible-sdk/saves#saveinfo), or `null` when there's none, from the SDK's copy in memory, without waiting.
```js
await arcadible.ready;
const info = arcadible.saves.infoSync("progress");
```
- **Throws** `not_ready` before `ready`, `unavailable` while the saves haven't loaded, and `invalid` for a bad name, as [`readSync`](https://developer.arcadible.com/references/arcadible-sdk/saves#saves.readSync) does.
- For a write still waiting on the play page, `savedAt` is this device's time until the page answers.
## `remove(name)`
```ts
remove(name: string): Promise
```
Removes the save `name`. Removing a save that doesn't exist isn't an error.
```js
await arcadible.saves.remove("slot-3");
```
- **Fails with** `invalid`, `full`, `busy`, `timeout`, or `unavailable`.
- After `ready`, the save is gone from the sync calls at once.
## `where`
```ts
readonly where: "game" | "build" | "memory" | string | undefined;
```
Who can read the saves back, once `ready` has settled. It doesn't change while the page is open.
| Value | Who can read them back |
| --- | --- |
| `"game"` | Every version of the game, in its channel: the play page keeps them, on the web, in the desktop app, or under `arc serve`. |
| `"build"` | Only this build: the game stands alone, keeping them on its own origin. On Arcadible, every version has an origin of its own, so the next version won't see them. |
| `"memory"` | Only until the page closes: the browser won't let them be kept, such as when it blocks site data. |
| `undefined` | Not known yet: before `ready`, and while the saves haven't loaded. |
```js
await arcadible.ready;
if (arcadible.saves.where === "memory") {
showNotice("Progress won't be kept after you close the game.");
}
```
- More values may be added. Treat a value you don't know as `"game"`.
## `SaveInfo`
```ts
type SaveInfo = {
version: string | null;
savedAt: string;
};
```
| Field | Value |
| --- | --- |
| `version` | The version of the game that wrote the save, stamped by the play page. `null` when written off Arcadible. |
| `savedAt` | When it was written, as an ISO 8601 time. |
Compare `version` with `arcadible.build.version` to [handle other versions' saves](https://developer.arcadible.com/build/saves#versions).
# Arcadible CLI
`arc` creates, validates, packages, and publishes Arcadible games. Any engine that outputs an `index.html` works.
Install it, log in, and ship a starter game:
```sh
npm install -g arcadible # requires Node.js 24+
arc login
arc init star-drift && cd star-drift
arc deploy
```
`arc` reads everything it deploys from your project: the README, the art in `.arcadible/`, and `arcadible.toml`. See [How Arcadible Works](https://developer.arcadible.com/concepts#game) for how they fit together, and [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github) for the `git` method.
## Workflow
Day to day, edit your game, its README, or its art, play it locally, bump the version, and deploy. While you're developing, use a prerelease version, so each build goes to `staging`:
```sh
arc serve # play it locally, as Arcadible runs it
arc deploy # validate, build, package, and publish
arc play -r staging # play the latest prerelease
```
[`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) runs every step itself, so you don't need to build or package first. When something's wrong, two commands show what the CLI sees:
```sh
arc validate # the same checks arc deploy runs, with reference links
arc inspect # the manifest after merging the README, media, package.json, and arcadible.toml
```
[`arc inspect`](https://developer.arcadible.com/references/arcadible-cli/arc-inspect) prints everything, HTML included, which can be a lot. Give it a dot path to see one part. A number picks a list's item, counting from 0, so an issue [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) reports in `game.gallery.3` is one command away:
```sh
arc inspect game.gallery # one field
arc inspect game.gallery.3 # one item in it
```
## Configuration
Every command except `login`, `logout`, and `whoami` accepts `-C, --cwd ` to run against another directory, and most accept `--json` for scripting and `--quiet` for CI. The project is that directory, or the one `arc` runs in, and it needs an `arcadible.toml`: `arc` never looks for one in the directories above ([`manifest_missing`](https://developer.arcadible.com/references/errors#manifest_missing)), except [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init), which creates it. [`arc open`](https://developer.arcadible.com/references/arcadible-cli/arc-open) and [`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) work anywhere when given a game; outside a project without one, they ask for it ([`game_id_missing`](https://developer.arcadible.com/references/errors#game_id_missing)).
### `.arcrc`
To set default options, use a `.arcrc` file with one section per command, named as the command is. Each option is a flag's long name, or a positional's, such as `method`:
```ini title=".arcrc"
[deploy]
method = git
timeout = 600
[play]
browser = true
```
A `.arcrc` can also be JSON, with an object per command: `{ "deploy": { "method": "git" } }`.
`arc` reads every `.arcrc` it finds, and a later file overrides an earlier one, option by option:
1. `/etc/arc/config` and `/etc/arcrc` (not on Windows)
2. `~/.config/arc/config`, `~/.arc/config`, and `~/.arcrc`
3. The nearest `.arcrc` in the working directory or above it
4. The `.arcrc` in the directory `-C` names
A flag on the command line overrides them all. Unlike the `rc` package's convention, environment variables and `--config` don't set options.
### Environment
| Variable | Effect |
| --- | --- |
| `NO_COLOR` | Turns off color, even on a terminal |
| `APPDATA` | On Windows, where the session is stored (`%APPDATA%\arc`) |
The session from [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) is stored in `~/.config/arc/account.json`, readable only by you.
## Output
The CLI is built to be run by people, scripts, CI, and coding agents alike:
- **Results go to stdout,** and everything else to stderr: progress, warnings, errors, and `… aborted.` messages. [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate)'s issues and warnings are its result, so they go to stdout.
- **Exit codes:** `0` when a command succeeds, `1` when it fails, and `130` when you cancel an [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) prompt. Each command's page lists what it returns.
- **Every command's output has the same shape:** a heading that says what happened (`Published v1.2.0 to production.`, `Deploy aborted.`), the reason on the next line, then any details, such as links or what to do next, each after a blank line:
```text
Deploy aborted.
Not logged in.
Run `arc login` to log in.
Code: not_logged_in
https://developer.arcadible.com/references/errors#not_logged_in
```
A failure ends with its code and its entry in [Errors](https://developer.arcadible.com/references/errors), a failed release's reason included, except an unknown option or a value of the wrong type, which, without `--json`, prints the command's help and the message.
- **Only the heading is red or green,** for failure or success.
- **With `--json`,** stdout holds exactly one JSON document, the result or `{ "error": { "code": "…", "link": "…", "message": "…" } }` (`link` is the code's entry in Errors, and the HTTP `status` is added when Arcadible answered with an error), and stderr stays empty. [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build), [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack), and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) also list their [warnings](https://developer.arcadible.com/references/errors#warnings). Match on the [code](https://developer.arcadible.com/references/errors#codes), which doesn't change, rather than the message, which can. Each command's page shows its JSON. A deploy whose release fails also includes its details and the [`release`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release).
- **With `--quiet`,** only errors are printed.
- **Spinners and progress bars** appear only when stderr is a terminal. Piped, in CI, or run by an agent, the output has no control codes.
- **Color** is used only on a terminal. Set `NO_COLOR` to turn it off there too.
## Commands
| Command | Description |
| --- | --- |
| [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) | Scaffold a project from a template, or prepare an existing one |
| [`arc inspect`](https://developer.arcadible.com/references/arcadible-cli/arc-inspect) | Print the resolved manifest |
| [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) | Check the project against the manifest rules |
| [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build) | Run the project's build command |
| [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack) | Pack the game into the zip `arc deploy` deploys |
| [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) | Serve the game locally, framed in a play page, as Arcadible does |
| [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) | Deploy the game, directly or through GitHub |
| [`arc open`](https://developer.arcadible.com/references/arcadible-cli/arc-open) | Open a game's store page |
| [`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) | Play a game in the Arcadible app or a browser |
| [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) | Log in with a device code |
| [`arc whoami`](https://developer.arcadible.com/references/arcadible-cli/arc-whoami) | Show the logged-in account |
| [`arc logout`](https://developer.arcadible.com/references/arcadible-cli/arc-logout) | Log out |
Every error message, and what to do about it, is in [Errors](https://developer.arcadible.com/references/errors). For the starter templates, see [Starter templates](https://developer.arcadible.com/build/game-engines#starter-templates).
# `arc`
List available commands.
## Usage
```sh
arc [options]
Commands:
arc init Initialize a project
arc inspect Print the resolved manifest
arc validate Check the project for issues
arc build Run the project's build
arc pack Pack the game into a zip
arc serve Serve the game locally
arc deploy Deploy the game
arc open Open a game's store page
arc play Play a game
arc login Log in to Arcadible
arc whoami Show the logged-in account
arc logout Log out of Arcadible
Options:
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Running `arc` with no command prints the command list. Add `-h` to any command to see its options:
```sh
arc deploy -h
```
The core loop is [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) while you work, then [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), followed by [`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play), with prerelease versions on `staging` while you develop. The [CLI overview](https://developer.arcadible.com/references/arcadible-cli) covers how a project is structured and how the CLI is configured.
## Output
The command list, as above.
| Code | When |
| --- | --- |
| `0` | It printed the command list |
| `1` | The command isn't one of `arc`'s |
# `arc init`
Initialize a project.
## Usage
```sh
arc init [project] [options]
Positionals:
project Project directory [string] [default: "."]
Options:
-t, --template Project template
Public GitHub repository, or a folder in one
(user/repo[/folder]) [string]
-y, --yes Accept prompt defaults [boolean] [default: false]
-j, --json Format output as JSON [boolean] [default: false]
-q, --quiet Output only errors [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Creates a new project, or prepares an existing one for Arcadible.
### New project
When `project` doesn't exist yet, `arc init` asks for a template and scaffolds it:
```sh
arc init star-drift
arc init star-drift -t arcadible/starters/phaser
arc init star-drift -t my-org/my-template # any public GitHub repository, or a folder in one
arc init star-drift -y # the blank starter, no prompts
```
| Template | Stack |
| --- | --- |
| `arcadible/starters/blank` | Blank canvas (default) |
| `arcadible/starters/threejs` | Three.js |
| `arcadible/starters/phaser` | Phaser |
| `arcadible/starters/pixijs` | PixiJS |
| `arcadible/starters/babylonjs` | Babylon.js |
The template question needs a terminal. In CI, scripts, and coding agents, pass `-t` or `-y`: without them, `arc init` fails with `No template chosen.` and exits with 1.
The template is copied without its Git history, and its `LICENSE` is removed so you can choose your own. `arc init` then writes a new [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id) to `arcadible.toml`, plus a [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version) of `0.1.0-beta.1` unless one can be derived, so the first deploy goes to staging.
### Existing project
When `project` already exists, including the default `.`, nothing is scaffolded. `arc init` only adds an `id`, and a `version` if one can't be derived, to `arcadible.toml`. It creates the file if needed and keeps your settings. When it adds either, it rewrites the file, so you lose its comments and formatting:
```sh
cd my-existing-game
arc init
```
Running it again is harmless: an existing `id` and `version` are kept.
## Output
It prints where the project is, and the template when it created one from a template. With `--json`:
```json
{
"project": { "path": "/home/you/star-drift", "template": "arcadible/starters/blank" }
}
```
| Code | When |
| --- | --- |
| `0` | The project was created, or its manifest updated |
| `1` | It failed, such as a template that couldn't be downloaded |
| `130` | You cancelled the template question |
# `arc inspect`
Print the resolved manifest.
## Usage
```sh
arc inspect [selector] [options]
Positionals:
selector Property selector [string] [default: null]
Options:
-j, --json Format output as JSON [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Prints the resolved manifest: the result of merging the README, the `.arcadible/` media, `package.json` if present, and `arcadible.toml`. This is exactly what [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) checks and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) publishes. See [Sources](https://developer.arcadible.com/references/arcadible-manifest#sources).
Use it to confirm where a value comes from, or why a field is missing:
```sh
arc inspect # everything, as TOML
arc inspect game # one table
arc inspect game.short_title # one value, by dot path
arc inspect game.gallery --json # as JSON
```
A dot path selects into tables and lists alike: a number picks a list's item, counting from 0. [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) names issues the same way, so for an issue in `game.gallery.3`, this shows which file it is:
```shell-session
$ arc inspect game.gallery.3
3 = ".arcadible/gallery/screenshot_02_phone.png"
```
A selector that doesn't match prints `# = null`, or `null` with `--json`. `game.description` is shown as the HTML extracted from the README, which is also what's measured against the 3,000-character limit.
## Output
The manifest as TOML, or with `--json`, as JSON. With a selector, only its value: `"1.2.0"` for `arc inspect game.version --json`.
| Code | When |
| --- | --- |
| `0` | It printed the manifest |
| `1` | The project's files can't be read, such as an `arcadible.toml` that isn't valid TOML |
# `arc validate`
Check the project for issues.
## Usage
```sh
arc validate [options]
Options:
-j, --json Format output as JSON [boolean] [default: false]
-q, --quiet Output only errors [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Checks the resolved manifest against the rules in the [Arcadible Manifest](https://developer.arcadible.com/references/arcadible-manifest) and [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown) references, including the media files' dimensions, aspect ratios, sizes, and durations. It runs locally and doesn't need you to be logged in.
```shell-session
$ arc validate
2 issues:
✗ game.description: Invalid Markdown.
Value 'http://example.com' isn't allowed for href on .
✗ game.icon: Invalid PNG.
Image is 500 × 400px.
Image must be at least 512px wide.
Image must be at least 512px tall.
Image must be 1:1.
2 references:
https://developer.arcadible.com/references/arcadible-manifest#game.description
https://developer.arcadible.com/references/arcadible-manifest#game.icon
```
Each field lists every rule it breaks, so one run shows everything to fix. A path is the exception: it stops at the first check it fails, such as `Must be within the project.`, since the checks after it depend on it. A media file that breaks a rule is described first, with its dimensions and, for a video, its length.
With no issues, it prints `No issues found.` It exits with `1` when there are issues or the project's files can't be read, and `0` otherwise, so it works as a pre-commit hook or CI step. Use `--json` to get the issues as `{ "issues": [...], "warnings": [...] }`, each with its code.
[`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) runs the same checks first and aborts if any fail.
### Warnings
Arcadible ignores a field or table in `arcadible.toml` it doesn't know, so `arc validate` warns about each one, with the name it might mean when one is close, and still exits with `0`:
```shell-session
$ arc validate
1 warning:
⚠ game.genre: Unknown field.
Did you mean: genres?
No issues found.
```
[`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build), [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack), and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) print warnings too, on stderr, and go on. `--quiet` leaves them out. With `--json`, each lists them under `warnings`, as `arc validate` does, along with its own, such as `arc build`'s [`deploy_dir_not_built`](https://developer.arcadible.com/references/errors#deploy_dir_not_built). See [Warnings](https://developer.arcadible.com/references/errors#warnings).
## Output
With `--json`, the issues and the warnings, each an empty list when there are none. Each has the field's `path`, the rule's [`code`](https://developer.arcadible.com/references/errors#validation), the `message` and its `details`, and the `link` to the field's rules, or `null` for a warning:
```json
{
"issues": [
{
"code": "media_invalid",
"details": ["Image is 500 × 400px.", "Image must be at least 512px wide."],
"link": "https://developer.arcadible.com/references/arcadible-manifest#game.icon",
"message": "Invalid PNG.",
"path": "game.icon"
}
],
"warnings": []
}
```
| Code | When |
| --- | --- |
| `0` | No issues |
| `1` | One or more issues, or the project's files can't be read, such as an `arcadible.toml` that isn't valid TOML |
What each message means is in [Errors](https://developer.arcadible.com/references/errors#validation).
# `arc build`
Run the project's build.
## Usage
```sh
arc build [options]
Options:
-j, --json Format output as JSON [boolean] [default: false]
-q, --quiet Output only errors [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Runs the project's [`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) command from the project root, after validating the project.
```sh
arc build
```
When the build succeeds, it prints the path of [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir), or, if the build didn't create it, warns `Build didn't create deploy_dir ().` ([`deploy_dir_not_built`](https://developer.arcadible.com/references/errors#deploy_dir_not_built)). When no build command is configured, nothing runs, and it prints `Nothing to do.`
You don't need to run this yourself: [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) and [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) run it, and so does the GitHub release workflow.
## Output
It prints `Built.` with the build's output folder, or `Nothing to do.` when there's no build command. If the build doesn't create [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir), the heading turns yellow and it warns. With `--json`:
```json
{ "artifact": { "path": "/home/you/star-drift/dist" }, "warnings": [] }
```
`artifact` is `{ "missing": "…" }` when `deploy_dir` wasn't created, with a [`deploy_dir_not_built`](https://developer.arcadible.com/references/errors#deploy_dir_not_built) warning, and `null` when there's no build command. `warnings` lists those and [`arc validate`'s](https://developer.arcadible.com/references/arcadible-cli/arc-validate#warnings).
| Code | When |
| --- | --- |
| `0` | The build ran, or there was nothing to do |
| `1` | The project has issues or its files can't be read, or the build failed or was stopped |
# `arc pack`
Pack the game into a zip.
## Usage
```sh
arc pack [options]
Options:
-i, --input Pack this directory instead of deploy_dir
[string] [default: null]
-o, --output Write the zip to this path [string] [default: null]
-j, --json Format output as JSON [boolean] [default: false]
-q, --quiet Output only errors [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Validates the project and packages it into the zip that gets uploaded.
```sh
arc pack
arc pack -o build.zip
arc pack -i dist # package a different directory than deploy_dir
```
The archive contains:
```text
-v.zip
└── -v/
├── index.html # the contents of deploy_dir (or --input)
├── …
├── .arcadible/ # the media directory, as is
└── arcadible.json # the resolved game manifest
```
Everything is inside one folder named like the zip.
- By default it's written to `.arc/package/-v.zip`. Keep `.arc/` in `.gitignore`: [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) refuses to release with untracked files.
- `index.html` must be at the root of the input directory, and `arcadible.json`, `.arcadible/`, and an `arcadible/` folder mustn't: they're Arcadible's ([`package_path_reserved`](https://developer.arcadible.com/references/errors#package_path_reserved)).
- `--input` changes only what goes in the zip. The project is still validated as a whole, so `deploy_dir` has to pass [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) too.
- The zip must fit Arcadible's [size limits](https://developer.arcadible.com/references/limits#package): 64 MB compressed for `deploy_dir`, 256 MB uncompressed with `.arcadible/`, up to 10,000 files, and no file over 64 MB.
- It doesn't run the build. Build first if `deploy_dir` is generated.
You don't need to run this yourself: [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) runs it, and so does the GitHub release workflow. It's mainly useful for checking exactly what will be uploaded.
## Output
It prints the zip's path, SHA-256, and size. With `--json`:
```json
{
"artifact": {
"path": "/home/you/star-drift/.arc/package/…-v1.2.0.zip",
"sha256": "d4eafacb…",
"size": 59206
},
"warnings": []
}
```
`warnings` lists [`arc validate`'s](https://developer.arcadible.com/references/arcadible-cli/arc-validate#warnings).
| Code | When |
| --- | --- |
| `0` | The zip was written |
| `1` | The project has issues or its files can't be read, `deploy_dir` or `--input` doesn't exist or has no `index.html`, or the game breaks a [package limit](https://developer.arcadible.com/references/limits#package) |
# `arc serve`
Serve the game locally.
## Usage
```sh
arc serve [options]
Options:
-p, --port Serve on this port [number] [default: 4100]
--build Run the build command first [boolean] [default: true]
--url Frame another server's page instead of deploy_dir [string]
-o, --open Open the play page in a web browser [boolean] [default: false]
-q, --quiet Output only errors [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Serves what [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) would publish, on your machine, the way Arcadible serves it: framed in a play page, with the [Arcadible SDK](https://developer.arcadible.com/references/arcadible-sdk). It runs [`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) first, then serves [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir) until you press Ctrl+C.
```shell-session
$ arc serve
Built and serving v1.2.0 locally.
Play:
http://localhost:4100/
Game only:
http://127.0.0.1:4100/
Reloads when .arcadible-public/ changes. Press Ctrl+C to stop.
```
Open the **Play** link. The play page and the game have origins of their own, as on Arcadible: the page is `localhost`, and the game `127.0.0.1`. When a file in `deploy_dir` changes, the game reloads in the page, and the page keeps its saves.
- **`--no-build`** serves `deploy_dir` as it is. Run your build tool's watch mode beside it, such as `vite build --watch`, and each rebuild reloads the game.
- **`--port`** picks another port, such as to serve two games at once ([`port_in_use`](https://developer.arcadible.com/references/errors#port_in_use)).
- **`--url`** frames another server's page, such as your engine's dev server, instead of serving `deploy_dir`. See [Framing a dev server](https://developer.arcadible.com/references/arcadible-cli/arc-serve#url).
- **`--open`** opens the play page in your browser.
It doesn't validate the project; run [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) for that.
### Framing a dev server
With `--url`, the local play page frames another server's page, so a dev server's own reloading runs inside it, with the SDK's host and the sandbox. Nothing is built or watched, since that server does both:
```sh
npm run dev # Vite, at http://localhost:5173
arc serve --url http://localhost:5173 # in another terminal
```
The play page is cross-origin isolated, as on Arcadible, so the page it frames must send these headers, or the browser won't load it, and `arc serve` warns:
```text
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: cross-origin
```
The engine starters' `vite.config.js` already sends them, on both `npm run dev` and `npm run preview`:
```js title="vite.config.js"
const headers = {
"Cross-Origin-Embedder-Policy": "require-corp",
"Cross-Origin-Resource-Policy": "cross-origin",
};
export default {
base: "./",
build: { chunkSizeWarningLimit: 2048, outDir: ".arcadible-public" },
preview: { headers },
server: { headers },
};
```
The other server is what the game runs on, so its paths and types are its own, not Arcadible's. Run `arc serve` without `--url` to check the build itself.
### Like Arcadible
- **The play page** frames the game with the same sandbox and permissions as Arcadible's, so dialogs, pop-ups, and forms are blocked as they are for players ([Runtime Limitations](https://developer.arcadible.com/build/runtime-limitations)).
- **Paths are case-sensitive,** even on a disk that isn't: a request for `ship.png` doesn't find `Ship.png`. A path ending in `/` gets its `index.html`, and a folder without the `/` isn't found.
- **The game is cross-origin isolated,** with Arcadible's headers, and each file has the type Arcadible gives it.
- **`/arcadible/` is Arcadible's,** with the SDK's hosted script at `/arcadible/sdk/v1.js`. Anything else there isn't found.
- **Nothing is cached,** unlike on Arcadible, so every reload gets your latest files.
### The Arcadible SDK
The play page runs Arcadible's own host for the SDK, so [saves](https://developer.arcadible.com/build/saves) work as on Arcadible, from the package or the hosted script:
- **`arcadible.build`** is `{ version, channel: "local" }`, with the `version` you're serving.
- **Saves are kept by the local play page,** apart from Arcadible's, and carry over from one build to the next, as they do between versions. Each game's are its own, by its `id`. A different port starts with none.
- **The SDK's files are fetched from Arcadible** each time `arc serve` starts, so they're the ones players get. When Arcadible can't be reached, the last copy is used, with a warning that says when it's from.
## Output
It prints the links, then `Reloaded at
# `arc deploy`
Deploy the game.
## Usage
```sh
arc deploy [method] [options]
Positionals:
method Deploy method [string] [choices: "direct", "git"] [default: "direct"]
Options:
-j, --json Format output as JSON [boolean] [default: false]
-q, --quiet Output only errors [boolean] [default: false]
-w, --wait Wait until it's published or fails [boolean] [default: true]
--timeout Seconds to wait [number] [default: 300, or 900 for git]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Publishes the current [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version) of the project. It validates, builds, packages, and publishes in one step.
```sh
arc deploy # build and deploy from this machine
arc deploy git # tag a release and let GitHub Actions build it
```
Both methods start by validating the project, and abort on any issue (`N issues found.`). A [warning](https://developer.arcadible.com/references/arcadible-cli/arc-validate#warnings), such as an unknown field, is printed, and the deploy goes on.
Each version can be published once, so bump `version` before every deploy. A version is taken only when its build is published: if the upload or Arcadible's processing fails, deploy the same version again. Both methods refuse a version that's already published, and say what's newest on each channel and what to bump to:
```shell-session
$ arc deploy
Deploy aborted.
Version 1.2.0 is already published.
Production: 1.2.0
Staging: 1.2.0-beta.3
Bump the version, for example to 1.2.1.
```
A prerelease version, like `1.2.0-beta.1`, is published to the `staging` channel, and any other version to `production`. See [Channels](https://developer.arcadible.com/concepts#channels), and [Limits](https://developer.arcadible.com/references/limits#releases) for how often you can deploy.
Both methods then [wait for the release](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release) until it's published or fails, and say which. When a build is published, Arcadible emails you its version and channel, and when a release fails, it emails you why.
### `direct`
The default method. It builds and uploads from your machine:
1. Checks that you're logged in (`Not logged in.`). An expired session is refreshed first, and when it can't be, the deploy aborts with `Session expired.` Both say to run [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login).
2. Checks that the [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id) and [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle) aren't used by another game: an `id` that's another account's is refused as [`game_unavailable`](https://developer.arcadible.com/references/errors#game_unavailable), and a handle another game has, or one held for another account, as [`game_handle_taken`](https://developer.arcadible.com/references/errors#game_handle_taken). A handle the [handle limits](https://developer.arcadible.com/references/limits#handles) hold back from this release warns ([`game_handle_limit_reached`](https://developer.arcadible.com/references/errors#game_handle_limit_reached), [`game_handle_lapsed`](https://developer.arcadible.com/references/errors#game_handle_lapsed)), and the deploy goes on without it. Then checks that this version hasn't been published yet (`Version … is already published.`).
3. Runs [`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build), then validates the project again, since a build can write files the manifest reads, such as art (`N issues found after the build.`).
4. Packages [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir), `.arcadible/`, and the resolved manifest into a zip, within Arcadible's [size limits](https://developer.arcadible.com/references/limits#package).
5. Uploads the zip, with a progress bar. Arcadible refuses a game that was deleted or belongs to another account (`Game unavailable: …`); give [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id) a new UUID to deploy it as a new game.
6. [Waits](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release) until Arcadible has published it. See [Releases](https://developer.arcadible.com/publish/releases#from-deploy-to-published) for how long that takes.
It then says which channel the build went to, and where it is:
```shell-session
$ arc deploy
Published v1.2.0 to production.
Store:
https://arcadible.com/play/star-drift
Play:
https://play.arcadible.com/star-drift/
```
When it ran [`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) first, the heading says so: `Built and published v1.2.0 to production.` With `--no-wait`, it stops after the upload: the heading is `Submitted v1.2.0 to production.` (or `Built and submitted …`), and the last line says `Arcadible publishes it once it's processed.`
A prerelease goes to `staging`, with its staging link, such as `https://play.arcadible.com/star-drift:staging/`, and no store link, since the store shows only production. With `--json`, the same details are printed as `{ "built": true, "channel": "production", "store": "https://arcadible.com/play/star-drift", "url": "https://play.arcadible.com/star-drift/", "version": { "to": "1.2.0" }, "warnings": [] }`, with `"built": false` when there's no build command, and `"store": null` for a prerelease. `version.to` is the version deployed. Once it has waited, it adds the [`release`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release).
### `git`
Builds in CI from a tagged commit instead of on your machine, so every release maps to a commit. It doesn't need you to be logged in: anyone who can push to the repository can release the game, which is how a team shares deploys.
It needs the game deployed once with `arc deploy`, its repository connected in Arcadible Studio, and the release workflow. See [Set it up](https://developer.arcadible.com/publish/deploying-from-github#set-it-up).
`arc deploy git`:
1. If there's a `package.json`, requires a lockfile (`package-lock.json`, `npm-shrinkwrap.json`, `pnpm-lock.yaml`, or `yarn.lock`).
2. Requires a Git repository whose root is the project, since the release workflow builds from there ([`project_not_repo_root`](https://developer.arcadible.com/references/errors#project_not_repo_root)), with no uncommitted changes: staged, unstaged, or untracked files that aren't ignored. The release is built from `HEAD`, so uncommitted work wouldn't be part of it.
3. Requires the release workflow, `.github/workflows/arcadible-release.yml`, in `HEAD`.
4. Requires `origin` to be reachable, and the tag `arcadible/release/v` not to exist, locally or on `origin`.
5. Checks that the game is on Arcadible (`… isn't on Arcadible yet.`) and that this version isn't published yet. These lookups are public, so they need no login. If Arcadible can't be reached, it warns ([`version_unchecked`](https://developer.arcadible.com/references/errors#version_unchecked)) and continues.
6. Checks that `origin` is the repository connected to the game in Arcadible Studio (`… isn't 's repository on Arcadible.`). This lookup is public too, and asks about `origin` by name, so it checks whoever pushes. If `origin` is a path on your machine, or Arcadible can't be reached, it continues.
7. Creates the annotated tag `arcadible/release/v` on `HEAD` and pushes it to `origin`.
8. [Waits](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release) while GitHub Actions builds it and Arcadible publishes it, then says `Published v1.2.0 to production.` with its links, as `direct` does. Anyone can wait, logged in or not: a release says whether it's published or failed, and why, to anyone. Only the game's owner also gets the link to Arcadible Studio, since only they can open it.
With `--no-wait`, it prints the tag it pushed, and where the build will be:
```shell-session
$ arc deploy git
Pushed arcadible/release/v1.2.0 to origin.
Store:
https://arcadible.com/play/star-drift
Play:
https://play.arcadible.com/star-drift/
GitHub Actions builds it, then Arcadible publishes it to production.
```
With `--json`, it prints the same details as `direct`, plus the `tag` and without `built`.
The push triggers the repository's release workflow, which builds the game and attaches it to a GitHub Release for Arcadible to publish. See [What the workflow does](https://developer.arcadible.com/publish/deploying-from-github#what-the-workflow-does). To make `git` your default, set `method = git` in [`.arcrc`](https://developer.arcadible.com/references/arcadible-cli#arcrc).
### Waiting for the release
By default, `arc deploy` waits until Arcadible has published the release or it has failed, so you know right away, and so does CI: it exits with `1` when the release fails. Pass `--no-wait`, or set it in `.arcrc`, to stop once the zip is uploaded or the tag is pushed:
```ini title=".arcrc"
[deploy]
wait = false
```
A failed release says why, and what to do about it:
```shell-session
$ arc deploy
Release failed.
Version 1.2.0 of this game already exists.
Raise game.version and release again.
Arcadible Studio:
https://studio.arcadible.com/games/3f0c9a52-8d1e-4f7b-9a26-5b1c0e7d4a18/builds
Code: version_exists
https://developer.arcadible.com/references/errors#version_exists
```
Its code is the release's reason, one of a fixed set. Each one, and how to fix it, is in [Release failure reasons](https://developer.arcadible.com/references/errors#release-reasons).
It waits up to 5 minutes for `direct` and 15 for `git`, which also waits for GitHub Actions; change it with `--timeout `. When the time runs out, it exits with `1` and says the release may still finish, with where to see it. For `git`, if no release has appeared, GitHub Actions hasn't released it yet or its workflow failed, so it points to the repository's Actions page. If the workflow passed, the **Arcadible** check on the tagged commit says why Arcadible didn't take it.
## Output
With `--json`, the result adds the `release`: `{ "build": "…", "channel": "production", "id": "…", "message": null, "reason": null, "status": "published", "version": "1.2.0" }`. A failed release or a timeout prints the same details with an `error` next to them: `{ …, "error": { "code": "…", "link": "…", "message": "…" }, "release": { … } }`, with the release's `reason` and `message`. Every result and error also lists the deploy's `warnings`, such as an [unknown field](https://developer.arcadible.com/references/errors#field_unknown) or a [version it couldn't check](https://developer.arcadible.com/references/errors#version_unchecked), in the same shape as [`arc validate`'s](https://developer.arcadible.com/references/arcadible-cli/arc-validate#output). A release's `status` is `pending`, `processing`, `published`, or `failed`; it only waits on the first two. A failed release's `error.code` is its reason, and a timeout's is `wait_timed_out`, whose `error.message` holds the links to Studio or to the repository's Actions page, or, for a `git` release when you're not the game's owner, points to the **Arcadible** check on the tagged commit. When a `git` deploy times out before its release appears, `release` is `null`.
| Code | When |
| --- | --- |
| `0` | The release was published, or, with `--no-wait`, uploaded or tag pushed |
| `1` | Anything else: a check, the build, the upload, or the push failed, the release failed, or the wait timed out |
Each message, and what to do about it, is in [Errors](https://developer.arcadible.com/references/errors).
# `arc open`
Open a game's store page.
## Usage
```sh
arc open [game] [options]
Positionals:
game Game handle or ID [string] [default: "self"]
Options:
--browser Open in a web browser [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Opens a game's store page.
```sh
arc open # this project's game
arc open star-drift # any game, by handle or ID
arc open --browser # arcadible.com instead of the app
```
With the default `self`, the game is the current project's [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle), or its [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id) when there's no handle. By default the page opens in the Arcadible app, falling back to `https://arcadible.com/play/` when the app isn't installed.
It prints where it opened, and the store page's URL:
```shell-session
$ arc open
Opened in the Arcadible app.
https://arcadible.com/play/star-drift
```
## Output
It prints where it opened the page, and the URL. It has no `--json`.
| Code | When |
| --- | --- |
| `0` | The page was opened, or its URL printed |
| `1` | There's no handle or `id`, or the project can't be read |
# `arc play`
Play a game.
## Usage
```sh
arc play [game] [options]
Positionals:
game Game handle or ID [string] [default: "self"]
Options:
-r, --release Release channel or version [string] [default: "production"]
--browser Open in a web browser [boolean] [default: false]
-q, --quiet Output only errors [boolean] [default: false]
-C, --cwd Set the working directory [string] [default: "."]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Plays a deployed game.
```sh
arc play # this project: production, or staging before the first release
arc play -r staging # the latest prerelease
arc play -r 1.2.0-beta.1 # a specific version
arc play star-drift # any game, by handle or ID
arc play --browser # play.arcadible.com instead of the app
```
With the default `self`, the game is the current project's [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle), or its [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id) when there's no handle.
Without `-r`, it plays `production`, or `staging` if the game has no production release yet. It aborts with `Game not found: …` when the game isn't on Arcadible, which it checks for another game and, without `-r`, for `self`. If Arcadible can't be reached, it plays `production`. `--release` takes a channel (`production` or `staging`) or an exact version, and maps to the play URL:
```text
https://play.arcadible.com/star-drift/ # production
https://play.arcadible.com/star-drift:staging/
https://play.arcadible.com/star-drift:1.2.0-beta.1/
```
Builds can't be deleted one by one, and each stays playable at its version link until its game is deleted. To roll back, deploy the earlier code again with a new, higher version.
By default the game opens in the Arcadible app, using the game's [`orientation`](https://developer.arcadible.com/references/arcadible-manifest#game.orientation): from your manifest with `self`, or from its latest production release otherwise. If the app isn't installed, it opens in the browser instead.
It prints where it opened, and the URL:
```shell-session
$ arc play
Opened in the Arcadible app.
https://play.arcadible.com/star-drift/
```
When it falls back to `staging`, it says so first, on stderr: `No production release yet, so opening staging.` With `--quiet`, it prints nothing.
## Output
| Code | When |
| --- | --- |
| `0` | The game was opened, or its URL printed |
| `1` | The game wasn't found, there's no handle or `id`, or the project's files can't be read |
# `arc login`
Log in to Arcadible.
## Usage
```sh
arc login [options]
Options:
--browser Open a web browser on Enter [boolean] [default: true]
--qrcode Show a QR code [boolean] [default: true]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Logs the CLI in to your Arcadible account using a device code. No password is entered in the terminal.
```shell-session
$ arc login
Logging in…
https://account.arcadible.com/link
Press Enter to open the link.
```
To approve the login, open the link and enter the code, or scan the QR code on a phone that's already logged in. Pressing Enter opens the link directly in your browser. The request expires after 10 minutes.
On headless machines or over SSH, use `--no-browser` to skip the Enter prompt, and `--no-qrcode` to hide the QR code. Without a terminal, such as in a script, there's no Enter prompt.
The session is saved to `~/.config/arc/account.json`, or `%APPDATA%\arc\account.json` on Windows, and refreshed automatically when it expires. Treat that file like a password.
### In CI
`arc login` needs a person to approve it, so CI can't log in. Release from CI with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) instead: GitHub Actions builds the release, with no login. Without a login, `arc deploy git` still checks the repository and waits for the release; only the game's owner also gets the link to Studio.
## Output
| Code | When |
| --- | --- |
| `0` | You're logged in |
| `1` | The request expired or failed. Run `arc login` again |
# `arc whoami`
Show the logged-in account.
## Usage
```sh
arc whoami [options]
Options:
-j, --json Format output as JSON [boolean] [default: false]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Prints the display name of the logged-in account, or `Not logged in.`
```sh
arc whoami
arc whoami --json # { "user": { "display_name": "…", "id": "…" } }, or { "user": null }
```
If the session has expired, it's refreshed first. When it can't be refreshed, `arc whoami` prints `Session expired.` and says to run [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login), or `{ "user": null }` with `--json`.
## Output
| Code | When |
| --- | --- |
| `0` | Always, logged in or not: check `user` in the JSON |
| `1` | Arcadible couldn't be reached to refresh the session |
# `arc logout`
Log out of Arcadible.
## Usage
```sh
arc logout [options]
Options:
-q, --quiet Output only errors [boolean] [default: false]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
```
## Description
Logs out by deleting the saved session (`~/.config/arc/account.json`, or `%APPDATA%\arc\account.json` on Windows). Running it while logged out isn't an error.
## Output
| Code | When |
| --- | --- |
| `0` | You're logged out, whether or not you were in |
# Limits
Every limit Arcadible applies, in one place. Each gives the value, what happens when you reach it, and what to do. The CLI checks most of them before anything is uploaded, so you hear about them from [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) or [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) rather than from a failed release.
## Your game's files
What [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack) zips: [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir), with the store art in `.arcadible/` and the manifest beside it. The 64 MB zip size counts `deploy_dir` alone; the other limits count everything packed.
| Limit | Value | When you reach it | What to do |
| --- | --- | --- | --- |
| Zip size | 64 MB | `Package is larger than 64 MB.` | Compress assets, or [stream](https://developer.arcadible.com/build/best-practices#load-fast) what isn't needed to start |
| Size, uncompressed | 256 MB | `Package is larger than 256 MB uncompressed.` | The same |
| One file | 64 MB | `'' is larger than 64 MB.` | Split the file, or compress it |
| Number of files | 10,000 | `Package has more than 10,000 files.` | Bundle small files together |
| `index.html` | Required | `Package is missing 'index.html'.` | Point `deploy_dir` at the folder your build writes `index.html` to |
| `arcadible/` | Reserved | `'arcadible/' is reserved for Arcadible.` | Rename the folder, and the paths that load from it |
Arcadible checks the uncompressed size, the number of files, each file's size, and the reserved folder again when it publishes a release. A zip over 272 MB fails as [`zip_too_large`](https://developer.arcadible.com/references/errors#release-reasons), and one breaking another limit as `zip_invalid`.
## Store page text
| Field | Length | Characters |
| --- | --- | --- |
| [`title`](https://developer.arcadible.com/references/arcadible-manifest#game.title) | 1–30 | Letters, digits, spaces, and `. , ! ? ' " ( ) : ; -` |
| [`short_title`](https://developer.arcadible.com/references/arcadible-manifest#game.short_title) | 1–15 | The same |
| [`tagline`](https://developer.arcadible.com/references/arcadible-manifest#game.tagline) | 1–30 | The same |
| [`bio`](https://developer.arcadible.com/references/arcadible-manifest#game.bio) | 1–180 | The same |
| [`description`](https://developer.arcadible.com/references/arcadible-manifest#game.description) | 1–3,000, as HTML | [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown#elements) |
| [`genres`](https://developer.arcadible.com/references/arcadible-manifest#game.genres) | 1–3 | From the list of genres |
| [`trailer`](https://developer.arcadible.com/references/arcadible-manifest#game.trailer) | Up to 255 | A YouTube or Vimeo URL |
[`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) reports each field over its limit, with a link to its rules. The `description` counts its HTML, tags and link URLs included, so it's shorter as text; `arc inspect game.description` shows the HTML that's counted.
## Store art
| Art | Aspect ratio | Size, in pixels |
| --- | --- | --- |
| [`icon`](https://developer.arcadible.com/references/arcadible-manifest#game.icon) | 1:1 | 512 × 512 to 2048 × 2048 |
| [`cover`](https://developer.arcadible.com/references/arcadible-manifest#game.cover) | 3:2 | 768 × 512 to 3072 × 2048 |
| [`banner`](https://developer.arcadible.com/references/arcadible-manifest#game.banner) | 2:1 | 1024 × 512 to 4096 × 2048 |
| [`poster`](https://developer.arcadible.com/references/arcadible-manifest#game.poster) | 2:3 | 512 × 768 to 2048 × 3072 |
| [`splash`](https://developer.arcadible.com/references/arcadible-manifest#game.splash) | 16:9 | 1024 × 576 to 4096 × 2304 |
| [Gallery](https://developer.arcadible.com/references/arcadible-manifest#game.gallery) | Any | 512 to 2048 on each side |
- **Images** are PNGs of up to 2 MB.
- **Videos** (each art's `_video`, and the gallery's) are MP4s of up to 15 MB, running 3 to 30 seconds. An art video has its art's aspect ratio and sizes.
- **Gallery:** 1 to 24 files, screenshots and description media together, with no two sharing a file name.
## Releases
| Limit | Value | When you reach it | What to do |
| --- | --- | --- | --- |
| Versions | Each publishes once | `Version 1.2.0 is already published.`, or `version_exists` | Bump [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version) |
| Builds per account | 30 an hour, and 100 a day | The release fails as `build_rate_limited` | Deploy again later |
| Games per account | 100, deleted games aside | Releasing a new game fails as `game_limit_reached` | Delete a game you no longer need in Studio |
| The same version, deployed again | Once every 5 minutes | [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) fails as `deploy_rate_limited` | Wait, then deploy again |
| Time to publish | 1 hour | The release fails as `release_expired` | Deploy again |
| `arc deploy`'s wait | 5 minutes, or 15 for `git` | `arc deploy` fails as `wait_timed_out`; the release may still finish | Check Studio or the release's GitHub check, or pass a longer [`--timeout`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release) |
A published build reaches all of Arcadible within 10 minutes.
## Saves
What a game can keep through the [Arcadible SDK](https://developer.arcadible.com/references/arcadible-sdk), for each player, on each device, in each channel. The SDK checks each limit before a save is sent, and the play page again before it's kept.
| Limit | Value | When you reach it | What to do |
| --- | --- | --- | --- |
| Saves per game | 32 | The write fails as `too_many` | Remove a save, or combine saves |
| Size | 1 MB, every save together, in UTF-8 bytes | The write fails as `too_large` | Save less |
| A save's name | 1 to 64 letters, digits, `-`, `_`, or `.` | The call fails as `invalid` | Rename it |
| A save | A well-formed string | The write fails as `invalid` | Serialize it, such as with `JSON.stringify` |
| Calls waiting | 64 at once | The call fails as `busy` | Wait for some to finish |
| An answer | 10 seconds | The call fails as `timeout` | Try again |
## Handles
A [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle) is 3 to 24 characters: lowercase letters, digits, and single hyphens, starting and ending with a letter or digit. It's unique among games, and some names are reserved.
| What | Limit | When it's reached |
| --- | --- | --- |
| A handle let go | Held for your account for 30 days, when it changes or its game is deleted | Another account's game can't take it, and links to it don't work meanwhile |
| Handles on unreleased games | 5 per account, across your games with no production release | The deploy goes ahead without the handle ([`game_handle_limit_reached`](https://developer.arcadible.com/references/errors#game_handle_limit_reached)) |
| An unreleased game's handle | 90 days from when it claimed it, with an email 14 days before | The handle lapses: anyone else can take it, and the game plays at its `id` links |
| A lapsed handle | Comes back to your account only with a production release, for 90 days | The deploy goes ahead without it ([`game_handle_lapsed`](https://developer.arcadible.com/references/errors#game_handle_lapsed)) |
- **A released game keeps its handle** for as long as it exists. A game is released once it has a production release, and a production release is never held back by the last three rows.
- **[`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) refuses a taken handle:** another game's, one held for another account, or a reserved one ([`game_handle_taken`](https://developer.arcadible.com/references/errors#game_handle_taken)). A release from [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) with a taken handle still publishes, and the game keeps the handle it had, or none.
- **Arcadible can reclaim a handle** that impersonates someone or infringes a name, or one held by a game that's never been released ([Community Guidelines](https://help.arcadible.com/legal/community-guidelines#handles)).
# Errors
Look up an error by its code, or by its message, to find what it means and what to do. The errors are grouped by where you see them: [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate), then the rest of the CLI, from logging in to waiting for a release, then a failed release, GitHub, and the warnings that don't fail anything. For a problem with no message, see [Troubleshooting](https://developer.arcadible.com/troubleshooting).
## How the CLI reports an error
A command that fails prints a heading naming what stopped, then the reason and what to do, then the error's code and a link to it on this page, and exits with `1`:
```shell-session
$ arc deploy
Deploy aborted.
Uncommitted changes found.
Commit, stash, or discard them first.
Code: git_uncommitted
https://developer.arcadible.com/references/errors#git_uncommitted
```
With `--json`, stdout holds the error as one JSON document, and stderr stays empty:
```json
{
"error": {
"code": "git_uncommitted",
"link": "https://developer.arcadible.com/references/errors#git_uncommitted",
"message": "Uncommitted changes found.\n\nCommit, stash, or discard them first."
},
"warnings": []
}
```
`link` is the code's entry on this page. An error from Arcadible's API adds the response's `status`. A command with a result to report alongside its error adds it to the same document, such as a failed release's `release`, and [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build), [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack), and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) add their [warnings](https://developer.arcadible.com/references/errors#warnings). See [Output](https://developer.arcadible.com/references/arcadible-cli#output).
### Codes
- **Match on the code, not the message.** Messages are written for people, and can change in any release. Codes don't.
- **A code never changes meaning,** and is never renamed or reused. One that's no longer used stays on this page.
- **New codes can be added in any release.** Treat a code you don't know as a failure, and show its message.
- **The CLI and releases share codes.** A [release failure reason](https://developer.arcadible.com/references/errors#release-reasons) is its code, so `version_exists` means the same thing from [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) and from a release.
## `arc validate` issues
[`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) lists every rule each field breaks, with a link to the field's rules. [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build), [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack), and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) run the same checks first, and stop with [`validation_failed`](https://developer.arcadible.com/references/errors#validation_failed) and the number of issues:
```text
✗ game.icon: Invalid PNG.
Image is 500 × 400px.
Image must be at least 512px wide.
```
With `--json`, each issue is an object, with the field's `path`, the rule's `code`, the `message`, its `details`, and the `link` to the field's rules, or `null` for a field the docs leave out:
```json
{
"issues": [
{
"code": "media_invalid",
"details": ["Image is 500 × 400px.", "Image must be at least 512px wide."],
"link": "https://developer.arcadible.com/references/arcadible-manifest#game.icon",
"message": "Invalid PNG.",
"path": "game.icon"
}
]
}
```
An issue's code names the rule it breaks, and follows the same [rules](https://developer.arcadible.com/references/errors#codes) as an error's: match on it, not the message. When `arc build`, `arc pack`, or `arc deploy` stops as [`validation_failed`](https://developer.arcadible.com/references/errors#validation_failed), its JSON has the same `issues` beside the `error`.
A [warning](https://developer.arcadible.com/references/errors#warnings), such as [`field_unknown`](https://developer.arcadible.com/references/errors#field_unknown), has the same shape, and is listed under `warnings` beside the issues. It doesn't fail the project.
### `required`
`Must be set.`
A required field is missing. For `id`, run [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init). For `version`, set one, or add it to `package.json`.
### `empty`
`Must not be empty.`, `Must not be blank.`
The field is set to `""` or `[]`, or a text field to only spaces. Remove it to use its default.
### `too_short`
`Must be at least N characters.`
The value is shorter than the field allows, such as a [`handle`](https://developer.arcadible.com/references/limits#handles) under 3 characters. See [Limits](https://developer.arcadible.com/references/limits). An empty value is [`empty`](https://developer.arcadible.com/references/errors#empty).
### `too_long`
`Must be at most N characters.`
Over the field's [limit](https://developer.arcadible.com/references/limits).
### `too_many`
`Must have at most N items.` (or `files`)
A list, such as `genres` or the gallery, has more items than the field allows. See [Limits](https://developer.arcadible.com/references/limits).
### `duplicate`
`Must not repeat an item.`
`Must not have two files of one name, whatever their extensions.`
A list names a value twice, or two gallery files share a name, such as `screenshot_1.png` and `screenshot_1.mp4`.
### `characters_invalid`
`Contains invalid characters: …`
A text field has a character outside its set, such as an emoji in a title, or a tab or zero-width character in the description. See [Limits](https://developer.arcadible.com/references/limits#text).
For the `handle`, it also covers its hyphens: `Must not start or end with a hyphen.`, and `Must not contain consecutive hyphens.` See [Handles](https://developer.arcadible.com/references/limits#handles).
### `option_invalid`
`Must be one of:` and the values it takes.
A field such as `genres`, `inputs`, or `audience` has a value that isn't in its list.
### `type_invalid`
`Must be a string, not a number.`
The value is the wrong kind, such as a number where text goes, or a single value where a list goes. Check the field's example in the [manifest reference](https://developer.arcadible.com/references/arcadible-manifest).
### `markdown_invalid`
`Invalid Markdown.`
The README's description uses an element, attribute, or HTML comment (``) Arcadible doesn't allow, or an attribute's value breaks its rule: a link that isn't `https`, or media that isn't a PNG or MP4 in `.arcadible/gallery/`, or doesn't exist. The details name each one. See [Elements](https://developer.arcadible.com/references/arcadible-markdown#elements).
### `description_rules_missing`
`Needs two --- rules around the description.`
The README has text after its title, tagline, and bio, but not two `---` rules around the description. Add them, or keep the README as it is and put the store page in `arcadible.md`. See [Structure](https://developer.arcadible.com/references/arcadible-markdown#structure).
### `path_invalid`
`Must be within .arcadible.`
A path in `arcadible.toml` points somewhere it can't: outside the project or `.arcadible/`, to a file that isn't there (`Must exist.`), to the project root, or for `deploy_dir`, somewhere that doesn't exist with no build to create it, or that has `.arcadible` anywhere in its path, or `arcadible.json`, `.arcadible`, or an `arcadible` folder at its root, which are reserved. The message names which.
### `index_missing`
`Must have an index.html at its root.`
[`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir) isn't the folder your build writes.
### `media_invalid`
`Invalid PNG.`, `Invalid MP4.`
A media file's size, dimensions, aspect ratio, or length is outside its rules, or it isn't a PNG or MP4. The details give the file's own and the rule it breaks. See [Media files](https://developer.arcadible.com/references/arcadible-manifest#media-files).
### `uuid_invalid`
`Must be a UUID.`, `Must be lowercase.`
`id` was edited. Restore it from Git, or run [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) for a new one.
### `version_invalid`
`Must be a version, such as 1.2.0.`, `Must be written as 1.2.0 or 1.2.0-beta.1: …`
`version` isn't a version such as `1.2.0`, or is written another way, such as `v1.2.0` or `1.2.0-Beta.1`.
### `url_invalid`
`Must be a YouTube or Vimeo URL.`
[`trailer`](https://developer.arcadible.com/references/arcadible-manifest#game.trailer) links somewhere else.
### `out_of_range`
`Must be N or less.`, `Must be N or more.`
A number is outside the values the field takes.
### `invalid`
Any other rule. The message says what's wrong.
## Logging in
### `not_logged_in`
`Not logged in.`
The command needs an account. Run [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login).
### `session_expired`
`Session expired.`
The saved session can't be renewed. Run [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) again.
### `login_code_expired`
`Login code expired.`
The code wasn't entered within 10 minutes. Run [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) again.
### `login_failed`
`Login request failed.`
Arcadible couldn't complete the login. Run [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) again.
## Your project
### `validation_failed`
`N issues found.`
The project doesn't pass [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate), which [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build), [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack), and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) run first. Run [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate), and fix each [issue](https://developer.arcadible.com/references/errors#validation). With `--json`, the issues are beside the error.
`arc deploy` also validates again after the build, since a build can write files the manifest reads, such as art: `N issues found after the build.` The files the build wrote stay, so `arc validate` reports the same issues.
### `parse_failed`
`Unable to parse .`
`arcadible.toml`, `package.json`, a [`.arcrc`](https://developer.arcadible.com/references/arcadible-cli#arcrc), or the README isn't valid TOML, JSON, INI, or Markdown. The parser's message follows: fix the syntax at the line it names.
### `path_not_found`
`Unable to find: `
A folder you named doesn't exist: the directory `-C` names, or the folder `arc pack --input` names. Check the path.
### `manifest_missing`
`No arcadible.toml in .`
The directory `arc` runs in, or the one `-C` names, has no `arcadible.toml`, so it isn't a project. `arc` reads only the directory it's given, never one above it. When a directory above has an `arcadible.toml`, the message names it: run `arc` there, or pass `-C` with it. To start a project, run [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init).
### `game_id_missing`
`No game handle or ID.`
[`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) and [`arc open`](https://developer.arcadible.com/references/arcadible-cli/arc-open) need a game. Pass its handle or `id`, or run them in the project, after [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) has added an `id`. Outside a project, with no game named, they ask for one this way too.
### `argument_invalid`
`Unknown argument: nope`
An option or value the command doesn't take, such as a misspelled option, a `--timeout` that isn't a number of seconds above 0, a `--port` that isn't a whole number from 1 to 65535, or a `--url` that isn't an `http` or `https` one. Run the command with `--help` for its options. Without `--json`, an option or value the command itself refuses (a misspelled option, say) prints the command's help and the message, with no code; a `--timeout`, `--port`, or `--url` it refuses ends like any other failure, with its code.
### `template_missing`
`No template chosen.`
[`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) had no terminal to ask in. Pass `-t `, or `--yes` for the blank starter.
### `template_unavailable`
`Unable to download template .`
The template couldn't be downloaded, or the folder it names isn't in the repository. Check that it's a public GitHub repository, or a folder in one, such as `arcadible/starters/phaser`. A starter's name on its own, such as `blank`, isn't one: [`arc init`](https://developer.arcadible.com/references/arcadible-cli/arc-init) suggests its full name.
### `game_not_found`
`Game not found: `
[`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) couldn't find the game. Check the handle. A game you haven't deployed isn't on Arcadible yet.
## Building and packing
### `deploy_dir_missing`
`Unable to find deploy_dir: `
[`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir) doesn't exist, so there's nothing to pack or serve. Run [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build) first. If the build ran, it writes somewhere else: set `deploy_dir` to that folder.
### `build_failed`
`Build failed with exit code N.`
[`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) failed, or was stopped (`Build stopped by SIGINT.`). The last 20 lines it printed follow, its standard output and then its error output, since some tools, such as `tsc`, print their errors to standard output. Fix the build, and run it on its own to check.
### `package_index_missing`
`Package is missing 'index.html'.`
[`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir) has no `index.html` at its root. Set it to the folder your build writes. When the build doesn't write it at all, [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build) warns [`deploy_dir_not_built`](https://developer.arcadible.com/references/errors#deploy_dir_not_built), and [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack) and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) stop with [`deploy_dir_missing`](https://developer.arcadible.com/references/errors#deploy_dir_missing).
### `package_path_reserved`
`'arcadible/' is reserved for Arcadible.`
The packed folder has an `arcadible` folder at its root, in any letter case. Every game's address keeps that folder for Arcadible's own files. Rename yours, and the paths that load from it. An `arcadible.json` or `.arcadible/` at its root is refused too, since packing adds its own: leave them out. [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) reports a [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir) that has it already. This catches one your build writes, or a folder you pack with `-i`.
### `package_too_large`
`Package is larger than 64 MB.`
`deploy_dir`, zipped, is over the [limit](https://developer.arcadible.com/references/limits#package). Compress assets, or [stream](https://developer.arcadible.com/build/best-practices#load-fast) what isn't needed to start.
### `package_uncompressed_too_large`
`Package is larger than 256 MB uncompressed.`
Everything packed is over the [limit](https://developer.arcadible.com/references/limits#package), `.arcadible/` included. Shrink the build or the art.
### `package_file_too_large`
`'' is larger than 64 MB.`
One file is over the [limit](https://developer.arcadible.com/references/limits#package). Split it, or compress it.
### `package_too_many_files`
`Package has more than 10,000 files.`
Over the [limit](https://developer.arcadible.com/references/limits#package). Bundle small files together.
## Serving locally
### `port_in_use`
`Port 4100 is in use.`
Something else is listening on the port [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) asked for, such as another `arc serve`. Stop it, or pass another port with `--port`.
## Before deploying
A version that's already published, and a game that was deleted or is another account's, are refused before anything is uploaded, as [`version_exists`](https://developer.arcadible.com/references/errors#version_exists) and [`game_unavailable`](https://developer.arcadible.com/references/errors#game_unavailable).
### `game_handle_taken`
`Handle taken: `
Another game, yours or another account's, has this [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle), or it's held for another account (a handle let go is held for 30 days), or it's reserved. Handles are unique among games: choose another. See [Limits › Handles](https://developer.arcadible.com/references/limits#handles). A game whose `id` is another account's fails as [`game_unavailable`](https://developer.arcadible.com/references/errors#game_unavailable) instead.
## `git` deploys
### `game_not_deployed`
` isn't on Arcadible yet.`
A `git` release can't create a game. Deploy once with [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), then [connect the repository](https://developer.arcadible.com/publish/deploying-from-github#set-it-up).
### `git_repo_missing`
`No Git repository found.`
The project isn't in a Git repository. Run `git init`, commit, and add a remote.
### `project_not_repo_root`
`Project is in , not the repository's root.`
The release workflow builds the project at the repository's root, so [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) releases only a project there. A game in a directory below it, such as one of [several games in one repository](https://developer.arcadible.com/studio/games#several-games-in-one-repository), deploys with [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy); or give it a repository of its own.
### `git_uncommitted`
`Uncommitted changes found.`
The release would be built from a commit that isn't what you have. Commit, stash, or discard the changes.
### `git_remote_missing`
`No Git remote origin.`
The repository has no `origin`. Add the game's GitHub repository: `git remote add origin `.
### `git_remote_unreachable`
`Unable to reach Git remote origin.`
`origin` is set, but Git couldn't reach it. Check your connection, the remote's URL (`git remote get-url origin`), and that you can access the repository.
### `lockfile_missing`
`No lockfile found.`
The project has a `package.json` but no lockfile, which the workflow installs from. Install your dependencies to create one, and commit it.
### `git_tag_exists`
`Git tag for v1.2.0 already exists, here or on origin.`
This version was released from Git before. Bump the version. After a failed run, you can delete the tag instead; see [GitHub](https://developer.arcadible.com/references/errors#github).
### `git_tag_create_failed`
`Unable to create Git tag for v1.2.0.`
Git couldn't tag `HEAD`. Git's own message follows, such as a lock file it found. Check that the repository has a commit, and that your Git name and email are set.
### `git_tag_push_failed`
`Unable to push Git tag for v1.2.0.`
Git couldn't push the tag. Git's own message follows, such as a hook's refusal. Check that you can push to `origin`.
## Uploading and waiting
### `upload_failed`
`Upload failed with status N.`
Arcadible's storage refused the zip, such as one changed after it was packed, or an outdated `arc`. Update `arc`, and deploy again.
### `api_refused`
`Not Found (404).`
Arcadible refused a request, with the status and, where it gives one, why. With `--json`, the error adds `status`. Check you're running the newest `arc` (`npm install -g arcadible`), and try again.
### `api_unreachable`
`Unable to reach Arcadible.`
A request got no response: you're offline, or something between you and Arcadible, such as a proxy or firewall, stopped it. Check your internet connection, and try again.
### `deploy_rate_limited`
`v1.2.0 was deployed in the last 5 minutes.`
The same version can be deployed once every 5 minutes. Wait, then deploy again. See [Limits](https://developer.arcadible.com/references/limits#releases).
### `wait_timed_out`
`Waited 5 minutes for v1.2.0.`
The release didn't finish within the wait, and may still finish. Check the link it prints to Studio (for `git`, when you're not the game's owner, the **Arcadible** check on the tagged commit), or pass a longer [`--timeout`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#waiting-for-the-release). For `git`, when no release appeared, check the repository's Actions tab; if the workflow passed, the **Arcadible** check on the tagged commit says why Arcadible didn't take it.
A release that fails prints `Release failed.` and what happened, then its reason as the error's code, with its link. See [Release failure reasons](https://developer.arcadible.com/references/errors#release-reasons).
## Release failure reasons
When Arcadible can't publish a release, the release fails with a reason. You see it in [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), as the error's code, in the "Release Failed" email, in Arcadible Studio, and, for `git`, in the Arcadible check on GitHub. See [Releases](https://developer.arcadible.com/publish/releases) for where each appears.
A version is taken only when its build is published. **Fix the cause and deploy the same version again,** unless the reason is `version_exists`. For `git`, delete the release's tag first; see [GitHub](https://developer.arcadible.com/references/errors#github).
### `version_exists`
`Version 1.2.0 is already published.`, `Version 1.2.0 of this game already exists.`
This version of the game already has a build. [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) checks before it uploads, and names the newest version in each channel and one to bump to. Bump [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version), and deploy again.
### `version_mismatch`
`The zip's game.version (1.2.1) isn't this release's version (1.2.0).`
The zip was packed from a different `version` than the one released. Deploy with [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), or [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git), which take the version from your manifest, rather than uploading or attaching a zip by hand.
### `game_unavailable`
`Game unavailable: `, `This game was deleted, or belongs to another account.`, `This account was deleted, so it can't release games.`
The game was deleted, or belongs to another account, or the account was deleted. Give [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id) a new UUID to deploy it as a new game. If it's your game, check you're logged in to the right account with [`arc whoami`](https://developer.arcadible.com/references/arcadible-cli/arc-whoami).
### `game_mismatch`
`The zip's game.id () isn't this release's game ().`
The zip's `id` isn't the game being released. Deploy with [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), which packs the zip from your manifest, or check the zip from [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack).
### `manifest_invalid`
`The build was refused: . Run arc validate.`
Arcadible refused the manifest the zip carries. Run [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate), fix what it reports, and deploy again.
### `zip_too_large`
`The zip is over 272 MB, the most a build can be.`
The zip is over 272 MB. Shrink the game within the [package limits](https://developer.arcadible.com/references/limits#package).
### `zip_invalid`
`The zip couldn't be read.`, `The zip has no arcadible.json.`, `The build has a folder 'arcadible/', which is reserved for Arcadible.`
The zip couldn't be read, breaks a [package limit](https://developer.arcadible.com/references/limits#package), has an `arcadible` folder at its root ([`package_path_reserved`](https://developer.arcadible.com/references/errors#package_path_reserved)), or has no `arcadible.json`, so it wasn't packed by `arc`. Run [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack) and check the zip opens, or deploy with [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), which packs it.
### `zip_missing`
`The release has no .zip asset.`
(`git`) The GitHub release has no zip attached, so there's nothing to publish. The starter's release workflow attaches one: keep `.github/workflows/arcadible-release.yml`, and release with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) rather than creating the GitHub release by hand.
### `repo_not_connected`
`/ isn't connected to a game.`
(`git`) The repository isn't connected to the game. The game's owner connects it on the game's **Repository** tab in [Arcadible Studio](https://studio.arcadible.com), and connects it again if it was renamed on GitHub. [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) checks that `origin` is the game's repository before it pushes the tag, whoever runs it (`/ isn't 's repository on Arcadible.`).
### `github_app_not_installed`
`The Arcadible GitHub App was disconnected from this account.`
(`git`) The Arcadible GitHub App was disconnected. Install it again from **Settings ▸ Integrations** in Arcadible Studio, then connect the repository again on the game's **Repository** tab. When the check says `This GitHub account isn't connected to Arcadible.`, the app is installed but not linked to your account: choose **Connect an existing installation** there.
### `build_rate_limited`
`This account reached its limit of 30 builds an hour.`
The account reached a [build limit](https://developer.arcadible.com/references/limits#releases): 30 builds an hour, or 100 in a day. Deploy again later.
### `game_limit_reached`
`This account reached its limit of 100 games.`
The release would create a game, and the account already has 100, its [limit](https://developer.arcadible.com/references/limits#releases). Deleted games don't count: delete a game you no longer need in [Arcadible Studio](https://studio.arcadible.com), then deploy again. New versions of your existing games still release.
### `release_expired`
`The release didn't finish within an hour.`
Deploy again. A release expires when its zip never finished uploading, or its processing never finished.
### `internal`
Something went wrong on Arcadible's side, in a release or in a request to Arcadible's API (a `5xx` status, which `--json` adds as `status`). Check [status.arcadible.com](https://status.arcadible.com) for an outage, then try again.
From the CLI itself, with no status, it's an error `arc` didn't expect: a bug. Update to the newest `arc`, and if it happens again, tell us at support@arcadible.com with the command and its output.
## GitHub
A `git` release can also fail on GitHub, before Arcadible has a release to report. These show on the tagged commit: in the workflow's run, or in the **Arcadible** check.
When the workflow's run fails, open it on the repository's **Actions** tab. A failed step's output is the CLI's, as above. The workflow has two checks of its own, each with why on the line under it: `Invalid tag: ` means the tag isn't `arcadible/release/v` and the project's `version` (release with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git), which tags it from the manifest), and `Unable to find: ` means the build didn't write [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir) as a folder: run [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build) locally and check where it writes.
When the **Arcadible** check fails, its title is `Release failed:` and the code, with a link to how to fix it. The code is a [release failure reason](https://developer.arcadible.com/references/errors#release-reasons), or one of these, when there's no release yet:
### `git_tag_invalid`
` isn't arcadible/release/v, with a version like 1.2.0 or 1.2.0-beta.1.`
The tag starts with `arcadible/` but doesn't name a version. Release with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git), which tags it from the manifest.
### `workflow_missing`
`The repository has no .github/workflows/arcadible-release.yml, which builds the release's zip and attaches it.`
[`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) checks before it pushes the tag (`No release workflow found: .github/workflows/arcadible-release.yml`), so you see this on GitHub only for a tag pushed another way. Add the workflow from your starter, and commit it. See [What the workflow does](https://developer.arcadible.com/publish/deploying-from-github#what-the-workflow-does).
A failed run publishes nothing, but its tag stays. To release the same version again, delete the tag locally and on `origin` (`git push origin :refs/tags/arcadible/release/v1.2.0`), or release a higher version.
## Warnings
A warning says something you might want to fix, and the command goes on. [`arc validate`](https://developer.arcadible.com/references/arcadible-cli/arc-validate) lists warnings with its issues; [`arc build`](https://developer.arcadible.com/references/arcadible-cli/arc-build), [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack), and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) print them in yellow on stderr. `--quiet` leaves them out. With `--json`, each command lists them under `warnings`, in the same shape as an [issue](https://developer.arcadible.com/references/errors#validation), with its `link` here (or, for `field_unknown`, `null`).
### `field_unknown`
`Unknown field.` (or `table`)
`arcadible.toml` has a field or table Arcadible doesn't know, such as a misspelled `genre`, or a `handle` above `[game]`. Arcadible ignores it. The details name the field it might mean, as in `Did you mean: genres?`, when one is close.
### `deploy_dir_not_built`
`Build didn't create deploy_dir ().`
[`workflow.build`](https://developer.arcadible.com/references/arcadible-manifest#workflow.build) ran and passed, but didn't write [`deploy_dir`](https://developer.arcadible.com/references/arcadible-manifest#workflow.deploy_dir), so there's nothing to pack. Set `deploy_dir` to the folder your build writes. [`arc pack`](https://developer.arcadible.com/references/arcadible-cli/arc-pack) and [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) stop with [`deploy_dir_missing`](https://developer.arcadible.com/references/errors#deploy_dir_missing).
### `game_handle_lapsed`
`Handle lapsed, so deploying without it.`
This game's [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle) lapsed: the game held it for 90 days without a production release. For 90 days after, your account can take it back only with a production release, while anyone else can take it at once. [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) deploys anyway, and the game plays at its `id` links. Release a production version to take the handle back, or choose another. See [Limits › Handles](https://developer.arcadible.com/references/limits#handles).
### `game_handle_limit_reached`
`Handle not applied, so deploying without it.`
Your games with no production release hold 5 handles between them, the most they can. [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) deploys anyway, and the game plays at its `id` links. Release this game to production, which takes the handle whatever the count, or release another one, or take the handle off another unreleased game. See [Limits › Handles](https://developer.arcadible.com/references/limits#handles).
### `version_unchecked`
`Unable to reach Arcadible to check v1.2.0, so pushing anyway.`
[`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) couldn't check that the game is on Arcadible and that the version isn't published, so it pushed the tag anyway. Arcadible checks again, and a published version fails as [`version_exists`](https://developer.arcadible.com/references/errors#version_exists).
# Troubleshooting
Start here when something isn't working and there's no error message to look up. For a message, see [Errors](https://developer.arcadible.com/references/errors).
## Releases
### My release didn't show up
Check where each step reports, in order:
1. **The CLI.** [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) waits until the release is published or fails, and says why it failed. If it timed out, the release may still finish.
2. **Arcadible Studio.** **Builds ▸ Failed** lists every failed release with its [reason](https://developer.arcadible.com/references/errors#release-reasons). A release still on its way is at the top of the game's **Builds** tab.
3. **Your email.** Arcadible emails "Release Published" or "Release Failed" for every release.
4. **GitHub,** for `arc deploy git`. If the workflow run failed, Arcadible never received the release: open the run on the repository's Actions tab. See [GitHub](https://developer.arcadible.com/references/errors#github).
A published build can take up to 10 minutes to reach all of Arcadible.
### My store page didn't change
- **The release was a prerelease.** Only a production release changes the store page. Arcadible Studio's **Store** tab shows what a prerelease would publish, under **Staging**. See [Channels](https://developer.arcadible.com/concepts#channels).
- **The release failed.** See [My release didn't show up](https://developer.arcadible.com/troubleshooting#my-release-didnt-show-up).
- **It hasn't reached you yet.** Changes take up to 10 minutes to reach all of Arcadible.
### My game isn't in the store or in search
It's unlisted, the default. Set [`visibility = "public"`](https://developer.arcadible.com/references/arcadible-manifest#game.visibility) and deploy a release version. See [Visibility](https://developer.arcadible.com/concepts#visibility).
## Playing on Arcadible
### My game works locally, but not on Arcadible
Your engine's dev server doesn't reproduce the [Arcadible sandbox](https://developer.arcadible.com/build/runtime-limitations), and [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) does: run it to find most of these on your machine. Check each of these:
- **File names.** Builds are served exactly as they are, and paths are case-sensitive: `Ship.png` isn't `ship.png`. See [Static files](https://developer.arcadible.com/build/runtime-limitations#static-files).
- **Routes and directories.** Every URL maps to one file: a client-side route such as `/level/3` fails, and so does a directory path without its trailing slash. Use the hash (`#/level/3`) for routes. See [Static files](https://developer.arcadible.com/build/runtime-limitations#static-files).
- **Assets from other servers.** Cross-origin isolation blocks anything served without CORS or a `Cross-Origin-Resource-Policy` header. See [Cross-origin isolation](https://developer.arcadible.com/build/runtime-limitations#cross-origin-isolation).
- **Precompressed builds.** Files that need the server to add `Content-Encoding` don't load. See [Compressed engine builds](https://developer.arcadible.com/build/runtime-limitations#compressed-engine-builds).
- **Dialogs, pop-ups, and forms.** `alert()`, `confirm()`, new windows, and form submission are blocked. See [The sandbox](https://developer.arcadible.com/build/runtime-limitations#the-sandbox).
- **Permissions.** Only some browser features are allowed, and some need a tap or click first. See [Permissions](https://developer.arcadible.com/build/runtime-limitations#permissions).
- **Saves.** Each version starts with empty browser storage, and the hosted Arcadible SDK script is served only on Arcadible and under `arc serve`. See [Saves](https://developer.arcadible.com/build/saves).
Open the browser's developer tools on the play page: errors from the sandbox name what was blocked. Deploy a prerelease to [staging](https://developer.arcadible.com/concepts#channels) to test on Arcadible without changing what players get.
### Players lost their saves after an update
- **The game keeps progress in browser storage.** It belongs to each build's origin, so a new version starts with it empty. Keep progress in [saves](https://developer.arcadible.com/build/saves), which carry over.
- **An older version overwrote a newer save.** Check a save's version before writing it. See [Handle other versions' saves](https://developer.arcadible.com/build/saves#versions).
- **The game is open in two tabs.** The last write wins. See [Two tabs](https://developer.arcadible.com/build/saves#tabs).
### `arcadible` is undefined
- **Under another local server,** the hosted script, `/arcadible/sdk/v1.js`, isn't found. Run the game with [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve), which serves it as Arcadible does, or use the `@arcadible/sdk` package, which works everywhere. See [Locally](https://developer.arcadible.com/build/saves#locally).
- **On Arcadible,** the script tag must come before your own scripts, without `async`, `defer`, or `type="module"`. See [Install](https://developer.arcadible.com/references/arcadible-sdk#install).
## The CLI
### `arc login` doesn't finish
- **The code expired.** A login request lasts 10 minutes. Run [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) again.
- **You're asked to prove you're human.** After 5 wrong codes, the link page asks for a captcha.
- **You're on a machine with no browser.** Open the printed link on any device, or scan the QR code with a phone that's logged in. See [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login).
### `arc play` opened a browser, not the desktop app
[`arc play`](https://developer.arcadible.com/references/arcadible-cli/arc-play) and [`arc open`](https://developer.arcadible.com/references/arcadible-cli/arc-open) use the Arcadible desktop app when it's installed, and a browser otherwise. Pass `--browser` to always use a browser.
# Arcadible Developer FAQ
## About Arcadible
### What are Arcadible Studio and the Arcadible CLI for?
The CLI, `arc`, creates, checks, and deploys games from your machine or from CI. Arcadible Studio is where you manage your games once they're published. See [How Arcadible Works](https://developer.arcadible.com/concepts).
### Where can players play my game?
In the Arcadible desktop app, and in any modern browser on desktop or mobile, from your game's play link or its store page. See [How Arcadible Works](https://developer.arcadible.com/concepts).
### Does it cost anything to publish a game on Arcadible?
No. Arcadible doesn't currently charge to publish or play games.
### Can I publish without the CLI?
No. A game's first release is always [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy). After that, a release can also come from a tag pushed to its GitHub repository, which [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git) creates for you. See [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github).
### How do I update the CLI?
Install the latest version with npm: `npm install -g arcadible@latest`. Check it with `arc --version`. See [Arcadible CLI](https://developer.arcadible.com/references/arcadible-cli).
## Building games
### Which engines and frameworks work with Arcadible?
Any engine or framework that builds to a folder with an `index.html`. See [Game Engines](https://developer.arcadible.com/build/game-engines) for the starters, and how to set up each kind of engine.
### Do I need the Arcadible SDK?
No. Your game is a web build, and Arcadible publishes it as it is. The Arcadible SDK is optional, for saves that carry over from one version to the next. See [Saves](https://developer.arcadible.com/build/saves).
### Does Arcadible provide leaderboards, achievements, or player accounts?
No. Arcadible doesn't currently provide these to games. Run them on your own server. See [Networking](https://developer.arcadible.com/build/runtime-limitations#networking).
### Can my game tell whether it's running in a browser or in the Arcadible desktop app?
No. Detect the capabilities you need instead, such as touch. See [Browsers and the desktop app](https://developer.arcadible.com/build/runtime-limitations#browsers-and-the-desktop-app).
### Should my game use relative or absolute paths?
Either works, because your game is served at the root of its own origin. Prefer relative paths, which also work from a subdirectory during local testing. See [How your game is loaded](https://developer.arcadible.com/build/runtime-limitations#how-your-game-is-loaded).
### How large can my game be?
`deploy_dir` can be up to 64 MB compressed, and the packed game, including `.arcadible/`, up to 256 MB uncompressed. See [Limits](https://developer.arcadible.com/references/limits#package).
### Can I use WebAssembly, WebGPU, and threads with `SharedArrayBuffer`?
Yes. Play pages are cross-origin isolated, so multithreaded exports, such as Godot with threads, run. Resources from other origins need CORS or CORP headers. See [Cross-origin isolation](https://developer.arcadible.com/build/runtime-limitations#cross-origin-isolation).
### Can my game make network requests to my own servers, or use WebSockets for multiplayer?
Yes, over HTTPS and WSS, with CORS that accepts any `*.arcadible.io` origin. WebSockets, WebTransport, and WebRTC data channels all work. See [Networking](https://developer.arcadible.com/build/runtime-limitations#networking).
### Can my game use gamepads, go fullscreen, lock the pointer or orientation, or autoplay audio?
Yes. In a browser, fullscreen, pointer lock, and audio need a click, tap, or key press first, and a gamepad appears once the player presses a button. The Arcadible desktop app plays audio without one. See [The sandbox](https://developer.arcadible.com/build/runtime-limitations#the-sandbox) and [Permissions](https://developer.arcadible.com/build/runtime-limitations#permissions).
### Can my game open popups, show `alert()` or `confirm()` dialogs, download files, or submit forms?
No. The Arcadible sandbox blocks them. See [The sandbox](https://developer.arcadible.com/build/runtime-limitations#the-sandbox) for what to do instead.
### Can my game work offline, or be installed as an app?
Not offline: each play loads the game from Arcadible. Players who want an app can play in the Arcadible desktop app. See [Browsers and the desktop app](https://developer.arcadible.com/build/runtime-limitations#browsers-and-the-desktop-app).
### How do I test my game locally the way it runs on Arcadible?
Run [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve). It builds your game, then serves it as Arcadible does, framed in a local play page with the sandbox, cross-origin isolation, case-sensitive paths, and the Arcadible SDK. Then deploy prereleases to `staging` to test on Arcadible itself. See [Testing Your Game](https://developer.arcadible.com/build/testing).
### Why does my game work locally, but not on Arcadible?
Your engine's dev server doesn't reproduce the sandbox, though [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve) does: paths are case-sensitive on Arcadible, assets from other servers need CORS, and dialogs and pop-ups are blocked. See [Troubleshooting](https://developer.arcadible.com/troubleshooting#my-game-works-locally-but-not-on-arcadible).
### Can I embed my game on my own website?
No. Play pages can't be embedded in other sites. Link to the play page or the store page instead. See [How your game is loaded](https://developer.arcadible.com/build/runtime-limitations#how-your-game-is-loaded).
### Can I use my own domain?
No. Games are played at `play.arcadible.com`, and their store pages are on `arcadible.com`. See [Links](https://developer.arcadible.com/concepts#links).
## Saves and player data
### Does save data carry over when I deploy a new version?
Saves made with the Arcadible SDK do. Browser storage doesn't: each build has its own origin, so each version starts with it empty. See [Saves](https://developer.arcadible.com/build/saves).
### Are saves kept in the player's account, or synced between devices?
No. Saves are kept on the player's device, separately in each browser and in the desktop app. See [Where saves are kept](https://developer.arcadible.com/build/saves#where).
### Can I use saves with Unity, Godot, or another engine's web export?
No. The Arcadible SDK doesn't currently have a plugin for engines. See [Add the SDK](https://developer.arcadible.com/build/saves#add-the-sdk).
### Can I test saves locally?
Yes, with [`arc serve`](https://developer.arcadible.com/references/arcadible-cli/arc-serve), from the package or the hosted script: its local play page keeps them, across builds, as Arcadible does. Under your own dev server, the package keeps saves on that server's origin, with the same rules, and the hosted script isn't found. See [Locally](https://developer.arcadible.com/build/saves#locally).
### Can my game identify the Arcadible player who's playing it?
No. Arcadible doesn't currently give games any information about the player. Use your own accounts on your own server. See [Networking](https://developer.arcadible.com/build/runtime-limitations#networking).
### Can my game use cookies?
No. Every build's origin is under `arcadible.io`, which browsers treat as one site, so other games could read or replace your game's cookies, and most browsers block or partition a frame's cookies anyway. Use [saves](https://developer.arcadible.com/build/saves) for progress, and tokens for your servers. See [Storage and saves](https://developer.arcadible.com/build/runtime-limitations#storage-and-saves).
## Projects and the manifest
### Does my project need a `package.json` or Node.js?
No. The CLI needs Node.js 24 or later, but your game doesn't. A `package.json`, if present, supplies `version` and the build command. See [Sources](https://developer.arcadible.com/references/arcadible-manifest#sources).
### If my project has a `package.json`, which version do I bump?
The one in `package.json`. A `version` in `arcadible.toml` overrides it, so leave it out to keep one version number. See [`version`](https://developer.arcadible.com/references/arcadible-manifest#game.version).
### Which fields in `arcadible.toml` are required?
Only `id` and `version`, and `version` can come from `package.json` instead. Every other field is optional. See [`game`](https://developer.arcadible.com/references/arcadible-manifest#game).
### Can I keep my repository README separate from my store page?
Yes. Put the store page in `arcadible.md`, which the CLI reads instead of `README.md`. See [The file](https://developer.arcadible.com/references/arcadible-markdown#the-file).
### Why is my title, tagline, or bio rejected?
They're plain text, limited to letters, digits, spaces, and `. , ! ? ' " ( ) : ; -`, and to a maximum length. Curly quotes, `&`, `/`, and emoji are rejected. See [Limits](https://developer.arcadible.com/references/limits#text).
### Why is my description over the limit when it has fewer than 3,000 characters?
The limit applies to the rendered HTML, so tags and URLs count too. See [`description`](https://developer.arcadible.com/references/arcadible-manifest#game.description).
### Can I publish several games from one repository?
Yes, with direct deploys: give each game its own directory and `arcadible.toml`, and deploy each with `arc deploy -C `. Git deploys support one game per repository, at its root. See [Several games in one repository](https://developer.arcadible.com/studio/games#several-games-in-one-repository).
### What happens if I change a game's `id`?
Arcadible treats the project as a different game. Commit the `id` and never change it. See [`id`](https://developer.arcadible.com/references/arcadible-manifest#game.id).
### Can I change my game's handle after it's published?
Yes, with a release version. Links with the old handle stop working, and links with the game's `id` keep working. See [`handle`](https://developer.arcadible.com/references/arcadible-manifest#game.handle).
### What counts as generative AI for the `ai` field?
Art, audio, text, and levels generated by a model, before release or during play, even if edited by hand afterward. Code written with an AI assistant doesn't count. See [Disclosing AI](https://developer.arcadible.com/publish/store-page#ai).
## Deploying and releases
### Should I deploy with `direct` or `git`?
Either. `direct` builds and deploys from your machine, and needs you to be logged in. `git` pushes a release tag that GitHub Actions builds, so every release maps to a commit, and anyone who can push to the repository can release without logging in. See [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github).
### How do I deploy from CI, without a browser?
Release with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git): GitHub Actions builds the release from a tag, with no login. Other CI providers can't currently deploy, since [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login) needs a person to approve it. See [Deploying from GitHub](https://developer.arcadible.com/publish/deploying-from-github).
### Do I need to install the Arcadible GitHub App to deploy with `git`?
Yes. Arcadible publishes a release only from a repository connected to the game: install the Arcadible GitHub App from **Settings ▸ Integrations** in Arcadible Studio, then connect the repository on the game's **Repository** tab. The game's first deploy has to be [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy), since a release can't create a game. See [Set it up](https://developer.arcadible.com/publish/deploying-from-github#set-it-up).
### Why can't I deploy the same version twice?
Each version identifies exactly one build, and play links can point at a version, such as `star-drift:1.2.0`. Bump the version before every deploy. If a deploy fails before its build is published, the version is still free. See [Versions, releases, and builds](https://developer.arcadible.com/concepts#releases).
### How long does it take for a deploy to become playable, and how do I know it succeeded?
A direct deploy is published within a few minutes, and a `git` deploy after its GitHub Actions run, which can take several minutes, depending on the build. [`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) waits until the release is published, then prints its links, or says why it failed and exits with `1`. Arcadible also emails you either way, and changes can take up to 10 minutes to reach all of Arcadible. See [Releases](https://developer.arcadible.com/publish/releases).
### Where do I see why a release failed?
[`arc deploy`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy) prints the reason, and so do the "Release Failed" email and **Builds ▸ Failed** in Arcadible Studio. Each reason, and its fix, is in [Errors](https://developer.arcadible.com/references/errors#release-reasons).
### Is there a limit on how often I can deploy?
Yes. An account can deploy up to 30 builds an hour, and 100 a day, and can have up to 100 games, not counting deleted ones. See [Limits](https://developer.arcadible.com/references/limits#releases).
### How do I roll back to an earlier version?
Deploy the earlier code again with a new, higher version. Earlier versions stay playable at their own links, such as `play.arcadible.com/star-drift:1.2.0/`. See [Rolling back](https://developer.arcadible.com/publish/releases#rolling-back).
### Can I delete a build?
No. Every build stays playable at its version link until you delete the game. See [Rolling back](https://developer.arcadible.com/publish/releases#rolling-back).
### Can I delete a game?
Yes, in Arcadible Studio: open the game, then **Delete game**. It's removed from Arcadible at once, with all of its builds, and it can't be undone. Its handle is held for your account for 30 days, and its `id` can't be deployed again; give the project a new `id` to publish it as a new game. See [Delete a game](https://developer.arcadible.com/studio/games#delete-a-game).
### Who can see and play staging builds?
Anyone with the link, such as `play.arcadible.com/star-drift:staging/`. Staging builds aren't shown in the store. See [On staging](https://developer.arcadible.com/build/testing#staging).
### Do staging builds update my store page?
No. Only release versions change the store page, and Arcadible Studio's **Store** tab shows what a staging build would publish, under **Staging**. A game's first deploy claims its handle, even a prerelease. See [Channels](https://developer.arcadible.com/concepts#channels).
### Is there a review before a game goes public?
No. A release is published as soon as it passes Arcadible's checks, and a public game is listed with its release. See [Going Public](https://developer.arcadible.com/publish/going-public).
## Store page
### How do I make my game public?
Set `visibility = "public"` and deploy a release version. See [Going Public](https://developer.arcadible.com/publish/going-public).
### How do players find my game?
Public games appear in the Arcadible store and in search. Unlisted games, the default, are playable only by link. See [Visibility](https://developer.arcadible.com/concepts#visibility).
### Why aren't my screenshots showing on the store page?
Only gallery files named `screenshot_`, with a lowercase `.png` or `.mp4` extension, appear in the carousel. Screenshots for another platform, such as `_phone`, are hidden on other devices. See [Screenshots](https://developer.arcadible.com/references/arcadible-manifest#screenshots).
### Do I need to provide every piece of art?
No. Every art field is optional. See [Media files](https://developer.arcadible.com/references/arcadible-manifest#media-files).
### Should my art include the game's title?
It can, on the cover, banner, and poster. Keep text off the icon and the splash, and leave taglines and slogans off everything. See [Text in art](https://developer.arcadible.com/publish/store-page#text-in-art).
### Why is my image or video rejected when it looks like the right size?
Aspect ratios are exact, so 1030 × 512 isn't 2:1. Also check the minimum and maximum dimensions, the file size, and that the file name is lowercase. See [Media files](https://developer.arcadible.com/references/arcadible-manifest#media-files).
### How do I make an art video loop smoothly?
Make its first and last frames match. Videos loop without sound, and run 3 to 30 seconds. See [Art](https://developer.arcadible.com/publish/store-page#art).
### Can I embed images or videos from other websites in my description?
No. Description media must come from `.arcadible/gallery/`. See [Media](https://developer.arcadible.com/references/arcadible-markdown#media).
### Can I localize my store page?
No. A store page has one title, tagline, bio, and description, in one language. See [Arcadible Markdown](https://developer.arcadible.com/references/arcadible-markdown).
### Are the files in `.arcadible/` publicly accessible?
Yes. They're published with the build, at `https://.arcadible.io/.arcadible/`. Don't put anything private there. See [Media files](https://developer.arcadible.com/references/arcadible-manifest#media-files).
### Can I link to my own website, social accounts, or a Discord server?
Yes, from the description, with `https://` links. See [Links](https://developer.arcadible.com/references/arcadible-markdown#links).
## Pricing
### Can I sell my game, or items in it?
No. Games on Arcadible are free to play, and Arcadible doesn't currently support paid games or in-game purchases.
## Studio and accounts
### What analytics does Arcadible Studio provide?
Only each game's plays and likes, on its **Overview**. Arcadible Studio doesn't currently provide other analytics. See [Arcadible Studio](https://developer.arcadible.com/studio).
### Can several people manage the same game?
No. A game belongs to one account. Anyone who can push to its connected repository can release it with [`arc deploy git`](https://developer.arcadible.com/references/arcadible-cli/arc-deploy#git). See [Ownership](https://developer.arcadible.com/studio/games#ownership).
### Can I transfer a game to another account?
No. Arcadible doesn't currently transfer games between accounts. See [Ownership](https://developer.arcadible.com/studio/games#ownership).
### Where does the CLI store my credentials?
In `~/.config/arc/account.json`, or `%APPDATA%\arc\account.json` on Windows. Treat that file like a password. [`arc logout`](https://developer.arcadible.com/references/arcadible-cli/arc-logout) deletes it. See [`arc login`](https://developer.arcadible.com/references/arcadible-cli/arc-login).
### How do I revoke a CLI session?
Run [`arc logout`](https://developer.arcadible.com/references/arcadible-cli/arc-logout) on that machine, which deletes its saved session. Arcadible doesn't currently revoke a session remotely. See [`arc logout`](https://developer.arcadible.com/references/arcadible-cli/arc-logout).
## Policies and support
### What content is allowed on Arcadible?
Games of every kind, including games for mature audiences, within the Community Guidelines. Sexually explicit content, real-money gambling, malware, and content that harms real people aren't allowed. See the [Community Guidelines](https://help.arcadible.com/legal/community-guidelines).
### Does marking a game `mature` change who can find or play it?
No. A `mature` game shows a notice on its store page, and is listed and playable like any other. See [Choosing an audience](https://developer.arcadible.com/publish/store-page#audience).
### Can I change my game's audience after it's published?
Yes. The audience is part of every release, so change it and deploy a release version. See [Choosing an audience](https://developer.arcadible.com/publish/store-page#audience).
### Who owns the rights to games published on Arcadible?
You do. Publishing gives Arcadible a license to host, show, and distribute your game on Arcadible while it's published. See the [Developer Terms](https://help.arcadible.com/legal/developer-terms).
### How do I report a game?
Choose **•••** ▸ **Report** on its store page. To add details, email [support@arcadible.com](mailto:support@arcadible.com) with the game's link and what's wrong. See [Report a game or a player](https://help.arcadible.com/contact#report-a-game-or-a-player).
### Where do I report a bug in Arcadible or get help?
Email [support@arcadible.com](mailto:support@arcadible.com), with the command you ran and its full output. Report a security problem to [security@arcadible.com](mailto:security@arcadible.com). See [Contact Us](https://help.arcadible.com/contact).