# Arcade full context Arcade turns terminal cells into a programmable canvas. It combines four separable layers: ## When to use this project Choose Arcade for terminal-first 3D rendering, reusable retained TUI components, browser-safe rendering primitives, or auditable self-play game loops. Choose another tool for GPU shaders, remote desktop streaming, authoritative multiplayer hosting, or a general public model-execution API. 1. Engine: meshes, OBJ parsing, geometry builders, CPU rasterization, materials, cameras, picking, animation, framebuffer effects, terminal display sampling, and Surface. 2. TUI: retained components, flexbox-like layout, focus, hit testing, scrolling, text input, scene/UI compositing, and cell diffs. 3. Game harness: authoritative Game and GameState contracts, perfect/imperfect information, legal action validation, model-safe context, AI players, animation synchronization, communication policy, and telemetry-safe canonical records. 4. Hosts: the raw terminal host serializes Surface to ANSI; the homepage runs that actual host in an isolated PTY; CanvasSurfaceHost remains available for focused browser surfaces. ## Recommended reading order - /docs — overview and import boundaries - /docs/getting-started — CLI installation, source workflow, Vercel sign-in, and library imports - /docs/app — launcher, human and model seats, match setup, live-game surfaces, and recovery - /docs/app/controls — offline-first Tutorial, camera gestures, global keys, contextual controls, and display modes - /docs/app/models — Vercel teams, generated Arcade key, availability-aware model picker, Gateway credits, spend, and troubleshooting - /docs/games — complete-game case studies and a build-your-own integration checklist - /docs/games/chess — rules, OBJ pieces, move choreography, model play, runner, and records - /docs/games/poker — Hold'em rules, cards, shuffling, chips, private observations, tournament runner, and records - /docs/games/islanders — building/trading rules, procedural world, model context, negotiation, and replay - /docs/rules — shared GameState, private information, chance nodes, and shipped game rules within the Games chapter - /docs/games/communication — ambient/autoreply policy, public chat, @model replies, privacy, and wisps - /docs/engine — renderer, materials, camera, display modes, imported models - /docs/renderer-pipeline — clipping, rasterization, post-processing, presenters, Surface, diffing, performance - /docs/game-visuals — shared production geometry, assets, layouts, and animation plans - /docs/platform — terminal input, color detection, raw mode, and the internal browser-opener boundary - /docs/tui — Surface, retained components, input, compositing - /docs/components — controls, tables, fields, overlays, themes, accessibility - /docs/game-harness — rules, model context, self-play, traces, agent tooling - /docs/tools — snapshot pipeline, exported headless runners, match-lab artifacts, model audits, benchmarks, privacy - /docs/web — Browser integration overview, CanvasSurfaceHost, focused scenes, responsive grids, and xterm parity - /docs/browser-host — Hosted CLI child page covering shell architecture, isolation, and credential boundaries - /docs/guides — complete render, terminal-app, custom-game, and visual-testing workflows - /docs/package-api — complete npm subpath map and public/private boundaries - /docs/reference — focused API contracts with signatures, defaults, lifecycle, errors, and examples - /docs/reference/symbols — generated exhaustive symbol index for every supported package entry point ## Using the Arcade app The `arcade` command launches a full-screen terminal app. Before entering full-screen mode, each interactive launch reuses the cached Vercel session and team or starts device authorization when no usable session exists. Its production Cover Flow contains Chess, Poker, Islanders, Leaderboard and Achievements covers marked coming soon, a Website shortcut, the Trailer, and an eight-chapter Tutorial. Development runs append internal test surfaces marked `dev only`; published installs hide them. A player can take a human seat against models, assign models to every available AI seat and spectate, or learn offline against Tutorial practice bots. Match setup remains open when a selected model fails its pre-start health check, so the player can switch the model or billed team and retry. The Tutorial runs on the real game screens and covers camera movement, menus, display modes, global keys, and the three games. It is offline-first, does not record practice-bot games, and leaves Gateway-only checklist steps out of completion when the player is signed out. Contextual controls are always available with `?`; `m` opens the menu, Escape closes the topmost layer, `d` cycles display modes, and the shared camera supports zoom, orbit, pan, arrow-key movement, and reset. Real model seats require Vercel device authorization and a selected team. Arcade automatically creates a team-scoped AI Gateway key named `Arcade ()`, or simply `Arcade` when the username is unavailable. The stored auth file contains the Vercel session and selected team, not the generated Gateway key; the key is re-derived for the running process on each launch. An inherited `AI_GATEWAY_API_KEY` is ignored so another shell credential cannot silently change the billing scope. The model picker requests an availability-aware catalog for the selected team and removes models that Gateway marks durably policy-ineligible. Unknown, transient, and configuration-dependent entries may remain visible, and a baked compatibility catalog is used if live discovery fails. Appearing in the picker is therefore not a routing guarantee; the Start-time health request is the current verdict for credits, rate limits, access, and provider health. Match setup suggests different creators across unfilled AI seats but leaves every model for the user to choose. Switching between play and spectate preserves committed model choices and re-deals only untouched seats. Teams that have never purchased AI Gateway Credits and are not on an eligible Enterprise plan use the Free tier. After card verification, AI Gateway grants $5 in free credits every 30 days for a subset of models with lower per-model rate limits. Purchasing at least $10 in Gateway Credits moves a team to the Paid tier, expands access beyond the Free-tier model subset, and raises rate limits; the recurring $5 credit then no longer applies. Eligible Enterprise teams can use invoiced billing instead. Current pricing, eligible models, keys, spend, logs, and budgets remain linked to the canonical Vercel AI Gateway pages. ## Game case studies The Games chapter follows complete products through the same four boundaries: presentation-independent rules, renderer-neutral game visuals, player-safe model context and bounded harnesses, then the production Arcade scene and HUD. Humans, models, headless policies, and visual playback all submit to the same legal-action and apply-action authority. ### Chess case study - `ChessState` owns the 0x88 board, legal movement, check, SAN history, repetition, terminal results, and SAN/UCI parsing. It detects checkmate, stalemate, fifty-move draws, threefold repetition, and insufficient material. - The six piece families load from OBJ assets through `fetchChessPieceMeshes(baseUrl, transport?)`; `measureChessPieceMeshes()` derives shared scale and square metrics. - `planChessMove(move, layout)` produces every visible segment for ordinary movement, captures, en passant, castling, jail placement, and knight arcs. Each segment carries its own `type` and `hideSq`. - Chess is perfect information. Models receive the current position, numbered SAN history, notation instructions, and optional public conversation. Responses are validated against legal moves before application. - `runChessMatch()` drives an animated scene; `runHeadlessChessMatch()` drives the same state directly. Both require two players, default to a 300-ply bound, expose chosen/applied hooks, and return `completed` or `bounded`. - `ChessMatchRecord` stores participants, controller assignments, diagnostics, SAN/UCI, move flags, final FEN, and result. Prompts, reasoning, and chat are excluded. ### Poker case study - `HoldemState` implements no-limit Texas Hold'em with private hole cards, flop/turn/river, legal betting order, full-raise reopening, short all-ins, side pots, showdown, and requested-versus-effective actions. - `evaluate()` selects the best five of five to seven cards and encodes category plus ordered kickers in one comparable integer. Categories run from high card through straight flush; A-2-3-4-5 is the ace-low wheel. - Card faces combine procedural rank/pip drawing with packaged PNG suit and court artwork. `preparePokerCardTextures()` resolves browser assets through `import.meta.url`; cards are double-sided strip-deformed geometry for peeking and physical riffle/bridge animation. - Chip inventory remains physical across stack, bet, pot, collection, and award positions. Creator wisps identify model seats and visibly react when that model speaks. - A model sees only its hole cards plus public board, street, blinds, pot, stacks, commitments, amount to call, minimum raise, all-in ceiling, and public action history. Private reasoning, selected move, and public speech are separate fields. - `runPokerSession()` carries stacks/button across hands, advances blind levels, emits lifecycle events, and stops on one remaining player or explicit hand/action bounds. It returns `PokerHandRecord[]`, one `PokerMatchRecord`, final stacks, winners, and blind progression. - Hand records preserve deals with visibility, requested/effective actions, side-pot settlement, awards, shown cards, and ending stacks. Match records preserve participants, controller changes, levels, eliminations, and placement. ### Islanders case study - `IslandersState` implements the base hex-island building-and-trading game for two to four seats: snake setup, production, robber/discard/steal, roads, settlements, cities, development cards, maritime and optional domestic trade, Longest Road, Largest Army, and victory at 10 points on the winner's turn. - A rolled 7 forces over-limit discards, then a robber move. If an eligible adjacent opponent exists, the actor must choose one and steal a random resource; theft is omitted only when no victim is eligible. - Canonical hex/node/edge topology lives with rules. Renderer-neutral visuals map IDs into procedural terrain, animated crops/trees/sheep, water/coast/surf, harbors, pieces, setup choreography, dice, and robber motion. - Each model receives its exact hand, development cards, actual/public VP and portfolio. Opponents expose only public VP and aggregate hidden-card counts. Board, buildings, roads, awards, recent turns, and exact legal actions are public context. - Initial-placement options expose neutral pips, diversity, numbers, harbors, and road expansion frontiers without ranking decisions for the model. - `IslandersTranscript` and `IslandersMatchRecord` preserve board/deck setup, applied actions, random outcomes, trade responses, awards, and final scores without storing prompts, private reasoning, or chat. ### Communication and chat Communication is proposed beside a legal move and never changes the move or rules state. - `autoreply`: the acting model may propose speech after every action; every spoken proposal is accepted, though the model may choose silence. - `ambient`: speech is scored against a 0.62 threshold using action salience, intent, spacing, repetition, and monologue frequency. It favors meaningful reactions, negotiation, and table politics while suppressing routine action narration. - Applied game actions become public `GameMoment` values. Ambient Islanders may offer one affected or relevant model a reaction opportunity; autoreply remains actor-focused. - `PublicConversation` sanitizes text, limits a message to 360 characters, stores addressed seats, and projects bounded recent public context per model. It never contains hidden hands, chain-of-thought, or account identity. - Live Chess, Poker, and Islanders games share a human chat composer. Type `@` to filter model labels and complete an exact `@model` mention. Chess and Poker queue one bounded reply for each addressed model; ambient Islanders does the same. Unaddressed messages become public context without forcing a reply, and model replies never create reply-to-reply obligations. - Canonical records and anonymous telemetry exclude chat, prompts, reasoning, voice, and raw model responses. Local Match Lab traces may separately retain communication decisions for debugging. - `CommunicationPolicy`, `PublicConversation`, `TableCommunicationCoordinator`, and the game-moment helpers are exported from `ascii-arcade/harness/communication`. ### Build your own game Define authoritative and replayable rules first; separate spectator and per-seat observations; add notation, structured decisions, canonical records, renderer-neutral visuals, a scene/HUD, and bounded tests. To ship inside Arcade, also register the launcher/setup mode, match driver and model-health path, Match Lab/model-audit adapters, snapshots, intentional package exports, sitemap routes, human docs, agent docs, and packed-package checks. ## Agentic capabilities Arcade is designed to be inspectable and executable by agents. Stable command IDs let humans and agents invoke the same UI verbs. Snapshot tooling renders deterministic PNGs without running the infinite TTY. Match-lab runs Chess, Poker, and Islanders with persistent local traces. Model compatibility audits distinguish structured-output support, fallback parsing, provider access, and team availability. Communication traces keep private reasons separate from public table talk. ## Safety and privacy Arcade resolves AI Gateway credentials through Vercel device authentication in both local CLI and hosted website sessions. Every fresh hosted Sandbox starts without cached Arcade authentication; the site owns no shared model credential, and the user's team-scoped key remains inside the isolated Arcade process rather than reaching the browser or visitor shell. Anonymous telemetry excludes prompts, reasoning, chat, voice, and account identity. Lightweight events use random install and session IDs, while human game records use a pseudonymous hash of the install ID for personal statistics. Hosted terminal and local Match Lab runs use ARCADE_TELEMETRY=0. ## Hosted terminal The home page connects xterm.js to a temporary Linux PTY containing the actual Arcade package packed from the same source revision as the site deployment. Users can run ordinary shell commands, browse the seeded docs/examples filesystem, and type `arcade` to launch the same CLI used after installation. The visitor shell and Arcade process run as separate unprivileged users, egress is limited to Vercel authorization and AI Gateway, each visitor supplies their own model credential through device login, and the session expires automatically. ## Browser surfaces The cinematic and hosted terminal reuse `ascii-arcade/web` and the packaged CLI instead of rebuilding Arcade in React. Complete game shells remain in the hosted CLI until an honest browser-safe boundary is extracted for them. ## Core contracts for agents ### Package boundaries - Public npm entry points are exactly the package export map: root, engine, engine/png, TUI, platform, web, generic/per-game rules, generic/per-game harnesses, communication, records, and generic/per-game game-visual modules. - `engine/png` is Node-only because it uses node:zlib. The root convenience entry, engine, browser TUI surface, game visuals, and web entry are browser-safe. - `src/arcade`, `src/tools`, `src/cinematic`, authentication, telemetry delivery, voice, and prism deployment are not npm APIs. Do not deep-import them. - Published match setup currently offers text-model seats only. The experimental realtime voice layer and Audio surface remain development-only. - Snapshot and match-lab orchestration are checkout tools. Their reusable foundations—renderer, Surface, Screen.snapshot, rules, Players, headless game runners, visual choreography, and canonical records—are public. ### Engine - `RenderTarget` owns float RGB color, depth, finite-depth bounds, resize, clear, and opaque/add/alpha plot behavior. - `Material` is the visual extension point: caller-owned typed uniforms, a vertex program, a fragment program that may discard, plus blend and cull state. - Rendering and presentation are separate. ASCII and hybrid modes normally sample 3 by 6 source pixels per terminal cell; pixel mode maps two vertical samples to a half-block cell. - Reuse targets, geometry, material data, and scratch buffers. Update transforms and bounded mutable attributes per frame instead of rebuilding static meshes. ### TUI - Nodes are disposable layout data; `Screen` retains focus, hover, press, pointer capture, mounted component instances, scene cache, and cell-diff history. - Stable IDs are the interaction identity boundary. Stateful controls such as `Input`, `Select`, `Dropdown`, `Slider`, and `ScrollBox` should be constructed once, mounted, and rendered through `Slot`. - `frameComposited()` is the scene-plus-UI path. It caches the scene layer and emits only changed terminal cells. - Dimensions and pointer positions are terminal cells, not pixels. Text uses terminal width rules for wide and combining characters. - Current limitations are intentional documentation: Input has no selection, Delete, Home/End, or pointer caret placement; Slider has a fixed 0-1 range; ScrollBox has no Home/End or ensureVisible API. ### Rules and harness - `GameState` is mutable and authoritative. It owns the current player, legal actions, state transition, terminal result, deep cloning, and canonical action parsing/formatting. - `TERMINAL` and `CHANCE` are special current-player values. Chance outcomes must be non-empty, finite, non-negative, and sum to one. - Imperfect-information prompts use player-safe information/observation methods, never complete debug state. - `MatchScene.playMove()` is the backpressure boundary: it resolves after the authoritative action and required visible animation settle. - `runMatch()` asks the active seat, awaits application, and then advances. Chosen hooks see pre-action state; applied hooks run after the scene settles. Inject `chanceRng` for deterministic tests and replay. - Canonical records store requested structured actions when relevant and authoritative applied actions for replay. They exclude prompts, reasoning, chat, voice, raw model responses, and account identity. ### Browser and platform - `CanvasSurfaceHost` renders a complete `Surface`; it does not own React, a scene, a render loop, or a ResizeObserver. - Call `resize(cssWidth, cssHeight, cols, rows)` when geometry changes. With `manageCssSize: false`, layout owns CSS size while the host owns backing pixels. - The terminal input parser accepts arbitrary chunks and buffers split escape sequences. Route normalized keys through `Screen.handleKey()` and convert one-based terminal mouse coordinates before screen dispatch. - Always pair terminal entry with cleanup. Static snapshots should bypass raw mode and terminal state entirely. ### Agentic development loop - Never capture `pnpm dev` to inspect graphics: it is an infinite raw-mode ANSI application. Run `pnpm snapshot help`, then render one bounded state with `pnpm snapshot:png`. - A scene snapshot freezes production render state at explicit terminal dimensions, time, seed, and interaction state; resolves supersampling/bloom; presents pixels as Surface cells; rasterizes terminal glyphs; and writes a PPM/PNG for inspection. - The repository does not yet export a one-call Surface-to-PNG function. `ascii-arcade/engine/png` encodes/decodes RGBA8 Texture data; the bundled terminal-font rasterizer remains snapshot-tool implementation. - Use `runHeadlessChessMatch`, `runHeadlessIslandersMatch`, `runIslandersInitialPlacement`, or `runPokerSession` to build a consumer-owned evaluator without importing Arcade UI. - Match-lab writes manifest.json first, then run-wide events.jsonl and per-match trace.jsonl incrementally, followed by result/canonical/summary artifacts. It always disables telemetry.