# Making a game for Fink

This file is for the coding agent (Claude Code, Cursor, Codex, Copilot, any
LLM) helping someone make a small web game for **Fink**
(https://finkinc.space), a city where people get better at real things. The
game hangs behind a door in Fink's Underground, and every minute someone
really plays it counts on their record as community XP. Read all of this
before you write code, and keep to it whatever else you are asked.

Two starters already do everything below: https://finkinc.space/kit/starters/2d/
(plain HTML, a question game) and https://finkinc.space/kit/starters/3d/
(three.js, the Fink figure walking). If this folder came from
`fink-starter.zip`, they are in `2d/` and `3d/`, with the kit for working
offline in `assets/` (the pages load it from finkinc.space first). Build on
one of them.

## The four rules

Every game keeps all four:

1. **Skill.** Your game trains a skill that's real outside the game.
2. **Effort.** Put work in: games that are obviously one prompt get taken down.
3. **No hate.**
4. **No obvious cheating.**

A game doesn't have to be great to stay up. It has to practise something
real (hearing intervals, mental arithmetic, reading a map, spotting a weak
argument, typing, a language's words), and the score has to measure that
skill, not luck, speed of clicking or time spent. "Effort" means the maker's
own ideas, levels, art and testing are in it: help them make it theirs, not
a first draft. No auto-play, no paying for idling, no inflated scores.

## What to build

- A static web page: `index.html`, plus any CSS, JS, images, sound, fonts or
  glTF files it needs. No server code; nothing runs on the server. A build
  step is fine if what you publish is its output.
- A title screen (the name, one sentence on what gets better, a Start
  button), then **rounds**: a few questions or one challenge, a score, a
  result line, and the next round. It gets harder as the player gets better.
- Mouse, keyboard **and** touch.
- Fink's look (below). Messages go on the page, never in a dialog.

## fink.js: connecting the game to Fink

One line, once, anywhere in the page. That is enough:

```html
<script src="https://finkinc.space/fink.js"></script>
```

Inside a Fink door (the page is opened with `#fink=<pass>` at the end of its
address) fink.js keeps a clock of **active** play: it starts at the player's
first input, counts only while the page is visible and the player has done
something in the last 5 minutes, and sends the time to Fink once a minute and
when the page is hidden or closed. 1 XP is 1 minute, every minute counts, and
there is no daily cap. A person has one clock, so two tabs don't count twice.

Two optional calls say more:

```js
fink.start();                                                   // start the clock now (a Start button)
fink.report({ score: 7, max: 10, result: "7 of 10 intervals" }); // at the end of each round
```

- `report` records how the round went and pays the time so far. `score` is
  what they got, `max` the most possible that round (above 0), `result` a
  few words. Once a round, not every second: Fink refuses more than 8
  reports a minute from one player, and doesn't believe a perfect round
  finished in under 15 seconds, so make a perfect round take longer.
- `fink.door.practice` is `true` outside a door; `fink.door.played()` is the
  seconds of active play so far.
- **Never** compute XP or send a time yourself, and never call
  `fink.login()`. Inside a door the game never learns who is playing.
- The game must work without Fink: check `window.fink` before a call, catch
  any error, and never make the player wait on one. Offline, fink.js isn't
  there at all.
- Opened any other way (double-clicked from disk, or on the maker's own site
  without a pass) the page runs in **practice**: every call works, nothing
  is sent, and a small tag in the corner says "practice".
- fink.js may be included twice safely. A maker playing their own door earns
  nothing; everyone else's minutes count.

## Fink's look

```html
<link rel="stylesheet" href="https://finkinc.space/kit/assets/fink-kit.css">
```

- Warm paper, white sheets with hairline borders, square corners, calm. Like
  a good old book: no gradients, glow, neon, rounded bubbly buttons or emoji
  decoration.
- Colours: only the stylesheet's variables (`--paper #f4e6db`, `--sheet`,
  `--sheet-2`, `--ink #1d2731`, `--ink-2`, `--ink-3`, `--rule`, `--rule-2`,
  `--link`, `--good`, `--bad`, `--warn`, `--gold`), never new ones. In a
  canvas or WebGL, use the same values from
  https://finkinc.space/kit/assets/palette.json. A branch's colour for a game
  that belongs to one: math `#0b3c5d`, music `#7a3c8c`, debate `#a2552b`,
  cs `#2f6b46`.
- Fonts: `var(--serif)` (Alegreya) for reading and big numbers, `var(--sans)`
  (Open Sans) for labels and buttons. Both come with the stylesheet.
- Parts: `f-sheet`, `f-btn--primary`, `f-chip`, `f-label`, `k-stage`,
  `k-board`, `k-big`, `k-choices` (with `k-right` / `k-wrong`), `k-result`,
  `k-meter`. Plain buttons are already styled. See https://finkinc.space/kit/.
- Pictures: only the kit's public-domain engravings
  (`https://finkinc.space/kit/assets/plates/<name>.jpg`, each shown with its
  maker and year under it, from `plates/credits.json`), the kit's pictures of
  Fink (`.../facades/<name>.jpg`, captioned "Fink"), or what you draw with
  CSS, SVG, canvas or WebGL in the colours above. No other images, nothing
  generated, nothing from a reference picture.
- The Fink figure for 3D: `https://finkinc.space/kit/assets/figure/fink-figure.glb`,
  the player's own figure from the city. It faces -Z, stands on y = 0 and is
  about 2 m tall; its joints are `Rig`, `HipJointL`/`R`, `ShoulderJointL`/`R`,
  `NeckJoint`, `Cap` (the top of the head, hinged) and `Key` (the wind-up key
  in its back). It walks by swinging the hip and shoulder joints about X, as
  the 3D starter does. Load three.js from cdn.jsdelivr.net with one pinned
  version.
- Words: a maker's place in the Underground is a *storefront*. Nothing in
  Fink is sold, so no talk of unlocking, buying, boosting or going premium.

## Don't break, don't crash

A game runs inside Fink's web player, on phones and on 5K screens. Keep to
this budget:

- **60 frames a second on an ordinary laptop.** Draw with
  `requestAnimationFrame` (or three.js `setAnimationLoop`), never a timer.
  Keep a 3D scene to a few hundred draw calls and a few hundred thousand
  triangles.
- **Cap the pixel ratio at 2**: `renderer.setPixelRatio(Math.min(devicePixelRatio, 2))`,
  and the same for a 2D canvas. A 5K screen at full ratio is 15 million
  pixels a frame.
- **No blocking loops.** Nothing that runs for more than a few milliseconds
  in one go; every `while` has a bound. Load big things asynchronously and
  show that they're loading.
- **Pause when hidden.** On `visibilitychange`, stop drawing and stop the
  sound while `document.hidden`; start again from where it was.
- **Memory.** Make meshes, textures and sounds once and reuse them; dispose
  what you remove (`geometry.dispose()`, `material.dispose()`,
  `texture.dispose()`); nothing that grows every frame.
- **Touch and keyboard.** Pointer events for mouse and touch together,
  `touch-action: none` on a canvas you drag on, targets at least 44 px, and
  keys for everything (and say which).
- **Every screen from 360 px wide to 5K.** Handle `resize`, no sideways
  scrolling, text that stays readable. Try 360 × 740 and 2560 × 1440.
- **No `alert()`, `confirm()` or `prompt()`.** The web player blocks them;
  put the message on the page.
- **Nothing that tracks or sells.** No analytics, trackers, ads or social
  widgets. Scripts only from cdn.jsdelivr.net, unpkg.com or
  cdnjs.cloudflare.com, pinned to a version.
- **Never ask who someone is.** No names, emails, passwords, ages, phone
  numbers or accounts, and no links to payment.
- **Sound only after a click or a tap.** Create or resume the `AudioContext`
  in the Start button's handler, and keep it quiet by default.
- **Credit every plate** with its maker and year, on the page.
- **Storage is for the game.** `localStorage` and IndexedDB work when Fink
  hosts it and on the maker's own site (a level reached, settings). Wrap them
  in `try { } catch { }`, as they can be switched off, and never store
  anything about who is playing.
- The page can't navigate the page above it or talk to it: links out open a
  new tab (`target="_blank" rel="noopener"`).

## Where it runs

**Hosted on Fink:** paste the page's code into the box at
https://finkinc.space/make, or drop one `.html` there, or a `.zip` with
`index.html` at its top (one wrapping folder is fine). At most 32 MB unpacked
and 300 files. Files it serves:
`html htm js mjs css json txt md png jpg jpeg gif webp svg ico woff woff2 ttf otf mp3 ogg wav m4a mp4 webm wasm glb gltf bin`.
Each game gets an address of its own (`d<door number>.` in front of Fink's
play domain), so its storage is its own. No cookies, no server code, static files
only; scripts from the CDNs above are fine. If the page doesn't load
fink.js, Fink adds the line itself, and a game Fink hosts wears a rim in the
Underground: its minutes always count. The box takes a whole page. A React
component on its own (a `.jsx` with imports and no HTML around it) is
refused: give one self-contained `index.html`.

**Linked:** any `https://` address you host yourself, on
GitHub Pages, Netlify, Vercel or anywhere. Include fink.js yourself, or no
minutes count. itch.io plays games inside its own frame, and Fink's pass may
not reach the game there, so minutes may not count on an itch build; Fink
hosting, GitHub Pages or Netlify count every one.

Everyone gets three games, free. Together they share 32 MB on Fink.

## Try it in practice

1. Open the page: double-click `index.html`, or, for anything that loads
   files (a glTF, a JSON level), serve the folder with
   `python3 -m http.server 8000` and open http://localhost:8000/.
2. Look for the small **"Fink · practice"** tag in the corner once you start:
   fink.js is there, every call works, and nothing is sent.
3. Play whole rounds: a perfect round takes longer than 15 seconds, and the
   score goes up only when the player does better at the skill.
4. Try it at phone width (360 px), with only the keyboard, by touch, in a
   big window, and hidden then shown again. The console stays free of errors.

## Publish

At https://finkinc.space/make put it in the box: paste the code or drop the
file or the zip (or, for a game on your own site, give its address), with a
name, the subject it is about (math, science, history, languages, reading and
writing, geography, music, computing, debate, arts, life skills, or anything
else; a game that belongs in one of Fink's halls can sit under a branch of
`music`, `math`, `debate` or `cs` instead, listed at
https://finkinc.space/domains), and one sentence on what gets better when you
play. It is on the shelf as soon as it is made: the Community page and the
Underground. Hang it in a painting in your Space if you like, and change it
any time from https://finkinc.space/doors/mine. Lain looks at every new game
and every new upload: one that keeps the four rules stays, one that breaks
them is taken down. The best are put on the front page, and a jam's best get
a plot above ground.

## Before you hand it over

- [ ] It trains one real skill, and the score measures it.
- [ ] Title screen, rounds, a result line, and it gets harder.
- [ ] One `<script src="https://finkinc.space/fink.js"></script>`; a
      `fink.report({ score, max })` at the end of each round, guarded.
- [ ] `fink-kit.css`, only the kit's colours and pictures, plates credited.
- [ ] Mouse, keyboard and touch; 360 px to 5K; pixel ratio at most 2;
      drawing stops while hidden.
- [ ] No `alert`, no login, no personal questions, no trackers, no sound
      before a click.
- [ ] It works opened from disk or a local server, with the "practice" tag.
