# Adding a game to Garden Street Games

This site has no build step. A "game" is just a folder of static files that
gets an `<iframe>` pointed at it. Follow this exactly and it'll work the
first time.

## 1. Export your build

**Godot (HTML5/Web export):**

- Project Settings → Export → Web. Use the official Web export template.
- Set the export **filename to `index.html`** — not `game.html`, not the
  project name. The site always loads `games/<slug>/index.html`.
- **Threading**: Godot 4's Web export defaults to a threaded build, which
  requires `SharedArrayBuffer` and therefore `Cross-Origin-Opener-Policy:
  same-origin` + `Cross-Origin-Embedder-Policy: require-corp` response
  headers on *every* asset the page loads. This is a static site with no
  server config by default (GitHub Pages can't set custom headers at all),
  so **the easy path is to export single-threaded**: in Export Options,
  set **Variant → Thread Support: off** (or uncheck "Thread Support" /
  disable the `Threads` feature, depending on your Godot version). A
  single-threaded export runs with zero special headers, on any static
  host, with no configuration. Only turn threading on if you've confirmed
  your target host serves COOP/COEP — see `docs/DEPLOY.md`.

  **If this site ever runs ads, treat single-threaded as a hard rule.**
  Cross-origin isolation (COOP + COEP `require-corp`) blocks any cross-origin
  subresource that doesn't send CORP/CORS headers — and ad tags, creatives and
  rewarded-video SDKs almost never do. Turning on threading would silently kill
  ad revenue on every page that hosts a game. Threading and third-party ads are
  effectively mutually exclusive.
- Leave "Canvas Resize Policy" at its default (Adaptive) so the game fills
  the iframe instead of rendering at a fixed pixel size.

**Plain JS/Canvas games:** just write `index.html` (plus any JS/CSS/assets
it needs) as a self-contained static page. Look at `games/_template/` and
`games/corner-street-run/` for the pattern: no external dependencies, no
build step, keyboard focus handled explicitly (see gotchas below).

## 2. Name and place the folder

Pick a kebab-case slug (e.g. `pepin-line`, `bluff-line`) and drop the whole
export into:

```
games/<slug>/index.html
games/<slug>/index.wasm      (Godot builds also ship .wasm/.pck/.js files)
games/<slug>/...
```

The slug is the folder name and must exactly match the `slug` field you add
in step 3. Don't nest it deeper — `games/<slug>/index.html`, not
`games/<slug>/build/index.html`.

## 3. Register it in `js/games-data.js`

Open `js/games-data.js` — the `GAMES` array is the **single source of
truth** for the whole site (catalog page, detail/player page, filters, all
of it). Append one object:

```js
{
  slug: 'your-slug',
  title: 'Display Title',
  tagline: 'One punchy line, under 60 chars',
  description: 'A paragraph for the detail page.',
  status: 'live',            // 'live' | 'in-development' | 'concept'
  year: 2026,
  tags: ['arcade', 'lake-city'],
  engine: 'Godot',           // 'Godot' | 'JS/Canvas' | 'TBD'
  embed: { path: 'games/your-slug/index.html', width: 1280, height: 720 },
  cover: 'assets/img/covers/your-slug.svg',
  accent: '#9BFF3C'          // optional, defaults to --green
}
```

- `embed.width` / `embed.height` set the aspect ratio the player locks to
  (16:9 is standard — match whatever your Godot viewport is set to).
- If the game isn't playable yet, set `status` to `'in-development'` or
  `'concept'` and `embed: null`. The detail page will show a "not playable
  yet" state instead of a broken iframe — don't point `embed` at a folder
  that doesn't have a real `index.html` in it.
- Add a cover image at `assets/img/covers/<slug>.svg` (or `.png`/`.jpg` —
  update the `cover` path to match). Until real art exists, copy the style
  of the existing placeholder covers: bold geometric shapes on `--ink`,
  using only the colors from `css/tokens.css`.

That's it — nothing else references the game list. The catalog and detail
pages read `GAMES` at load time.

## The export settings that actually matter

Learned the hard way exporting `pioneering`. All four of these fail in ways
that do not point at themselves.

- **`renderer/rendering_method.web` must be `gl_compatibility`.** The Web
  platform cannot run Forward+. Setting only `renderer/rendering_method.mobile`
  is NOT enough — web reads its own `.web` override and otherwise falls back to
  the base method, which defaults to `forward_plus`. In `project.godot`:

  ```
  [rendering]
  renderer/rendering_method.web="gl_compatibility"
  ```

  Expect Compatibility to silently drop **SSAO** and soft shadow penumbra. It
  does not warn; surfaces facing away from the sun just crush darker than they
  do in the editor, because there is far less ambient fill.

- **Turn VRAM texture compression off unless the project enables the formats.**
  `vram_texture_compression/for_mobile=true` requires
  `rendering/textures/vram_compression/import_etc2_astc=true` in project
  settings. Without it the export dies with `Cannot export project with preset
  "Web" due to configuration errors:` and **no error text after the colon** —
  a genuinely unhelpful message. If the game has no textures, set both
  `for_desktop` and `for_mobile` to `false`.

- **`variant/thread_support=false`.** See the threading note below.

- **The ~38 MB `.wasm` will not upload to Cloudflare as-is.** Cloudflare caps a
  single asset at 25 MiB. The fix is to ship `index.wasm.gz` and decompress it
  in the page with a small fetch shim — copy the block at the top of
  `games/pioneering/index.html`. Setting `Content-Encoding` in `_headers` does
  NOT work and fails silently with a hung loader; `docs/DEPLOY.md` has the
  evidence. This affects every Godot game, not just the first one.

Export from the command line once the preset exists:

```
/Users/tyleryotter/Downloads/Godot.app/Contents/MacOS/Godot \
  --headless --path . --export-release "Web" build/web/index.html
```

This needs the export templates installed at
`~/Library/Application Support/Godot/export_templates/<version>/`. They are not
installed with the editor — Editor → Manage Export Templates → Download and
Install, or drop `web_nothreads_{debug,release}.zip` in by hand.

## Godot-specific gotchas that will bite you

- **Must be served over HTTP, never opened as a `file://` URL.** Godot's
  Web export loads its `.wasm`/`.pck` via `fetch()`, which browsers block
  under `file://`. Always test with a local server — see
  `docs/DEPLOY.md` for the exact command that works on this machine. If
  the canvas stays black with console errors about `fetch` or CORS,
  you're almost certainly opening the file directly instead of through a
  server.
- **`SharedArrayBuffer` / cross-origin isolation.** Covered above — export
  single-threaded to avoid needing COOP/COEP headers at all. If you must
  ship a threaded build, the *whole page tree* (site + game) needs those
  headers on every response, which several static hosts can't do. Read
  `docs/DEPLOY.md` before attempting a threaded export.
- **Canvas resize and focus inside an iframe.** The site embeds your game
  in a responsive `<iframe>`, not a fixed-size window. Leave Godot's
  Canvas Resize Policy on Adaptive (the default) so it tracks the iframe's
  size. Keyboard input inside an iframe only reaches the game once the
  iframe (and the canvas inside it) has focus — the player page's
  click-to-start overlay handles the first click for you, but Godot
  itself should also grab focus on its own canvas on start (this is
  default behavior in the Web export; don't disable "Focus Canvas On
  Start" if your version exposes it).
- **Keep the export filename `index.html`.** The site never special-cases
  filenames — it always requests `games/<slug>/index.html`. Anything else
  is a 404 inside the iframe.
- **Relative paths only.** Don't hardcode absolute paths or a domain in
  your export — Godot's default relative asset references are what makes
  the folder portable to `games/<slug>/`.

## Testing before you register a game

1. Serve the whole site locally (see `docs/DEPLOY.md` — this machine does
   not have `python3` working; use the `ruby` one-liner there instead).
2. Open `games/<slug>/index.html` directly in the browser first, to
   confirm the export itself runs before worrying about the iframe.
3. Then add the entry to `js/games-data.js` and open
   `pages/game.html?slug=<slug>` to confirm it plays embedded, resizes,
   and fullscreens correctly.
